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
38 #define TOOL_CTRL_ID_TOUCH_YES 7
39 #define TOOL_CTRL_ID_TOUCH_NO 8
40 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
42 #define NUM_TOOL_BUTTONS 10
44 // constants for number of doors and door parts
46 #define NUM_PANELS NUM_DOORS
47 // #define NUM_PANELS 0
48 #define MAX_PARTS_PER_DOOR 8
49 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
50 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
53 struct DoorPartOrderInfo
59 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
61 struct DoorPartControlInfo
65 struct DoorPartPosInfo *pos;
68 static struct DoorPartControlInfo door_part_controls[] =
72 IMG_GFX_DOOR_1_PART_1,
77 IMG_GFX_DOOR_1_PART_2,
82 IMG_GFX_DOOR_1_PART_3,
87 IMG_GFX_DOOR_1_PART_4,
92 IMG_GFX_DOOR_1_PART_5,
97 IMG_GFX_DOOR_1_PART_6,
102 IMG_GFX_DOOR_1_PART_7,
107 IMG_GFX_DOOR_1_PART_8,
113 IMG_GFX_DOOR_2_PART_1,
118 IMG_GFX_DOOR_2_PART_2,
123 IMG_GFX_DOOR_2_PART_3,
128 IMG_GFX_DOOR_2_PART_4,
133 IMG_GFX_DOOR_2_PART_5,
138 IMG_GFX_DOOR_2_PART_6,
143 IMG_GFX_DOOR_2_PART_7,
148 IMG_GFX_DOOR_2_PART_8,
154 IMG_BACKGROUND_PANEL,
171 // forward declaration for internal use
172 static void UnmapToolButtons(void);
173 static void HandleToolButtons(struct GadgetInfo *);
174 static int el_act_dir2crm(int, int, int);
175 static int el_act2crm(int, int);
177 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
178 static int request_gadget_id = -1;
180 static char *print_if_not_empty(int element)
182 static char *s = NULL;
183 char *token_name = element_info[element].token_name;
188 s = checked_malloc(strlen(token_name) + 10 + 1);
190 if (element != EL_EMPTY)
191 sprintf(s, "%d\t['%s']", element, token_name);
193 sprintf(s, "%d", element);
198 int correctLevelPosX_EM(int lx)
201 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
206 int correctLevelPosY_EM(int ly)
209 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
214 int getFieldbufferOffsetX_RND(int dir, int pos)
216 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
217 int dx = (dir & MV_HORIZONTAL ? pos : 0);
218 int dx_var = dx * TILESIZE_VAR / TILESIZE;
221 if (EVEN(SCR_FIELDX))
223 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
224 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
226 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
227 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
229 fx += (dx_var > 0 ? TILEX_VAR : 0);
236 if (full_lev_fieldx <= SCR_FIELDX)
238 if (EVEN(SCR_FIELDX))
239 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
241 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
247 int getFieldbufferOffsetY_RND(int dir, int pos)
249 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
250 int dy = (dir & MV_VERTICAL ? pos : 0);
251 int dy_var = dy * TILESIZE_VAR / TILESIZE;
254 if (EVEN(SCR_FIELDY))
256 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
257 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
259 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
260 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
262 fy += (dy_var > 0 ? TILEY_VAR : 0);
269 if (full_lev_fieldy <= SCR_FIELDY)
271 if (EVEN(SCR_FIELDY))
272 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
274 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
280 static int getLevelFromScreenX_RND(int sx)
282 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
285 int lx = LEVELX((px + dx) / TILESIZE_VAR);
290 static int getLevelFromScreenY_RND(int sy)
292 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
295 int ly = LEVELY((py + dy) / TILESIZE_VAR);
300 static int getLevelFromScreenX_EM(int sx)
302 int level_xsize = level.native_em_level->lev->width;
303 int full_xsize = level_xsize * TILESIZE_VAR;
305 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
307 int fx = getFieldbufferOffsetX_EM();
310 int lx = LEVELX((px + dx) / TILESIZE_VAR);
312 lx = correctLevelPosX_EM(lx);
317 static int getLevelFromScreenY_EM(int sy)
319 int level_ysize = level.native_em_level->lev->height;
320 int full_ysize = level_ysize * TILESIZE_VAR;
322 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
324 int fy = getFieldbufferOffsetY_EM();
327 int ly = LEVELY((py + dy) / TILESIZE_VAR);
329 ly = correctLevelPosY_EM(ly);
334 static int getLevelFromScreenX_SP(int sx)
336 int menBorder = setup.sp_show_border_elements;
337 int level_xsize = level.native_sp_level->width;
338 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
340 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
342 int fx = getFieldbufferOffsetX_SP();
345 int lx = LEVELX((px + dx) / TILESIZE_VAR);
350 static int getLevelFromScreenY_SP(int sy)
352 int menBorder = setup.sp_show_border_elements;
353 int level_ysize = level.native_sp_level->height;
354 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
356 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
358 int fy = getFieldbufferOffsetY_SP();
361 int ly = LEVELY((py + dy) / TILESIZE_VAR);
366 static int getLevelFromScreenX_MM(int sx)
368 int level_xsize = level.native_mm_level->fieldx;
369 int full_xsize = level_xsize * TILESIZE_VAR;
371 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
374 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
379 static int getLevelFromScreenY_MM(int sy)
381 int level_ysize = level.native_mm_level->fieldy;
382 int full_ysize = level_ysize * TILESIZE_VAR;
384 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
387 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
392 int getLevelFromScreenX(int x)
394 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
395 return getLevelFromScreenX_EM(x);
396 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
397 return getLevelFromScreenX_SP(x);
398 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
399 return getLevelFromScreenX_MM(x);
401 return getLevelFromScreenX_RND(x);
404 int getLevelFromScreenY(int y)
406 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
407 return getLevelFromScreenY_EM(y);
408 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
409 return getLevelFromScreenY_SP(y);
410 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
411 return getLevelFromScreenY_MM(y);
413 return getLevelFromScreenY_RND(y);
416 void DumpTile(int x, int y)
422 printf_line("-", 79);
423 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
424 printf_line("-", 79);
426 if (!IN_LEV_FIELD(x, y))
428 printf("(not in level field)\n");
434 token_name = element_info[Feld[x][y]].token_name;
436 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
437 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
438 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
439 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
440 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
441 printf(" MovPos: %d\n", MovPos[x][y]);
442 printf(" MovDir: %d\n", MovDir[x][y]);
443 printf(" MovDelay: %d\n", MovDelay[x][y]);
444 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
445 printf(" CustomValue: %d\n", CustomValue[x][y]);
446 printf(" GfxElement: %d\n", GfxElement[x][y]);
447 printf(" GfxAction: %d\n", GfxAction[x][y]);
448 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
449 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
453 void DumpTileFromScreen(int sx, int sy)
455 int lx = getLevelFromScreenX(sx);
456 int ly = getLevelFromScreenY(sy);
461 void SetDrawtoField(int mode)
463 if (mode == DRAW_TO_FIELDBUFFER)
469 BX2 = SCR_FIELDX + 1;
470 BY2 = SCR_FIELDY + 1;
472 drawto_field = fieldbuffer;
474 else // DRAW_TO_BACKBUFFER
480 BX2 = SCR_FIELDX - 1;
481 BY2 = SCR_FIELDY - 1;
483 drawto_field = backbuffer;
487 static void RedrawPlayfield_RND(void)
489 if (game.envelope_active)
492 DrawLevel(REDRAW_ALL);
496 void RedrawPlayfield(void)
498 if (game_status != GAME_MODE_PLAYING)
501 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
502 RedrawPlayfield_EM(TRUE);
503 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
504 RedrawPlayfield_SP(TRUE);
505 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
506 RedrawPlayfield_MM();
507 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
508 RedrawPlayfield_RND();
510 BlitScreenToBitmap(backbuffer);
512 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
516 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
519 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
520 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
522 if (x == -1 && y == -1)
525 if (draw_target == DRAW_TO_SCREEN)
526 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
528 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
531 static void DrawMaskedBorderExt_FIELD(int draw_target)
533 if (global.border_status >= GAME_MODE_MAIN &&
534 global.border_status <= GAME_MODE_PLAYING &&
535 border.draw_masked[global.border_status])
536 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
540 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
542 // when drawing to backbuffer, never draw border over open doors
543 if (draw_target == DRAW_TO_BACKBUFFER &&
544 (GetDoorState() & DOOR_OPEN_1))
547 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
548 (global.border_status != GAME_MODE_EDITOR ||
549 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
550 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
553 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
555 // when drawing to backbuffer, never draw border over open doors
556 if (draw_target == DRAW_TO_BACKBUFFER &&
557 (GetDoorState() & DOOR_OPEN_2))
560 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
561 global.border_status != GAME_MODE_EDITOR)
562 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
565 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
567 // currently not available
570 static void DrawMaskedBorderExt_ALL(int draw_target)
572 DrawMaskedBorderExt_FIELD(draw_target);
573 DrawMaskedBorderExt_DOOR_1(draw_target);
574 DrawMaskedBorderExt_DOOR_2(draw_target);
575 DrawMaskedBorderExt_DOOR_3(draw_target);
578 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
580 // never draw masked screen borders on borderless screens
581 if (global.border_status == GAME_MODE_LOADING ||
582 global.border_status == GAME_MODE_TITLE)
585 if (redraw_mask & REDRAW_ALL)
586 DrawMaskedBorderExt_ALL(draw_target);
589 if (redraw_mask & REDRAW_FIELD)
590 DrawMaskedBorderExt_FIELD(draw_target);
591 if (redraw_mask & REDRAW_DOOR_1)
592 DrawMaskedBorderExt_DOOR_1(draw_target);
593 if (redraw_mask & REDRAW_DOOR_2)
594 DrawMaskedBorderExt_DOOR_2(draw_target);
595 if (redraw_mask & REDRAW_DOOR_3)
596 DrawMaskedBorderExt_DOOR_3(draw_target);
600 void DrawMaskedBorder_FIELD(void)
602 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
605 void DrawMaskedBorder(int redraw_mask)
607 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
610 void DrawMaskedBorderToTarget(int draw_target)
612 if (draw_target == DRAW_TO_BACKBUFFER ||
613 draw_target == DRAW_TO_SCREEN)
615 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
619 int last_border_status = global.border_status;
621 if (draw_target == DRAW_TO_FADE_SOURCE)
623 global.border_status = gfx.fade_border_source_status;
624 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
626 else if (draw_target == DRAW_TO_FADE_TARGET)
628 global.border_status = gfx.fade_border_target_status;
629 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
632 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
634 global.border_status = last_border_status;
635 gfx.masked_border_bitmap_ptr = backbuffer;
639 void DrawTileCursor(int draw_target)
645 int graphic = IMG_GLOBAL_TILE_CURSOR;
647 int tilesize = TILESIZE_VAR;
648 int width = tilesize;
649 int height = tilesize;
651 if (game_status != GAME_MODE_PLAYING)
654 if (!tile_cursor.enabled ||
658 if (tile_cursor.moving)
660 int step = TILESIZE_VAR / 4;
661 int dx = tile_cursor.target_x - tile_cursor.x;
662 int dy = tile_cursor.target_y - tile_cursor.y;
665 tile_cursor.x = tile_cursor.target_x;
667 tile_cursor.x += SIGN(dx) * step;
670 tile_cursor.y = tile_cursor.target_y;
672 tile_cursor.y += SIGN(dy) * step;
674 if (tile_cursor.x == tile_cursor.target_x &&
675 tile_cursor.y == tile_cursor.target_y)
676 tile_cursor.moving = FALSE;
679 dst_x = tile_cursor.x;
680 dst_y = tile_cursor.y;
682 frame = getGraphicAnimationFrame(graphic, -1);
684 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
687 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
688 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
690 if (draw_target == DRAW_TO_SCREEN)
691 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
693 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
697 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
699 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
702 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
704 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
705 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
707 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
710 void BlitScreenToBitmap(Bitmap *target_bitmap)
712 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
713 BlitScreenToBitmap_EM(target_bitmap);
714 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
715 BlitScreenToBitmap_SP(target_bitmap);
716 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
717 BlitScreenToBitmap_MM(target_bitmap);
718 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
719 BlitScreenToBitmap_RND(target_bitmap);
721 redraw_mask |= REDRAW_FIELD;
724 static void DrawFramesPerSecond(void)
727 int font_nr = FONT_TEXT_2;
728 int font_width = getFontWidth(font_nr);
729 int draw_deactivation_mask = GetDrawDeactivationMask();
730 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
732 // draw FPS with leading space (needed if field buffer deactivated)
733 sprintf(text, " %04.1f fps", global.frames_per_second);
735 // override draw deactivation mask (required for invisible warp mode)
736 SetDrawDeactivationMask(REDRAW_NONE);
738 // draw opaque FPS if field buffer deactivated, else draw masked FPS
739 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
740 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
742 // set draw deactivation mask to previous value
743 SetDrawDeactivationMask(draw_deactivation_mask);
745 // force full-screen redraw in this frame
746 redraw_mask = REDRAW_ALL;
750 static void PrintFrameTimeDebugging(void)
752 static unsigned int last_counter = 0;
753 unsigned int counter = Counter();
754 int diff_1 = counter - last_counter;
755 int diff_2 = diff_1 - GAME_FRAME_DELAY;
757 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
758 char diff_bar[2 * diff_2_max + 5];
762 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
764 for (i = 0; i < diff_2_max; i++)
765 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
766 i >= diff_2_max - diff_2_cut ? '-' : ' ');
768 diff_bar[pos++] = '|';
770 for (i = 0; i < diff_2_max; i++)
771 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
773 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
775 diff_bar[pos++] = '\0';
777 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
780 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
783 last_counter = counter;
787 static int unifiedRedrawMask(int mask)
789 if (mask & REDRAW_ALL)
792 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
798 static boolean equalRedrawMasks(int mask_1, int mask_2)
800 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
803 void BackToFront(void)
805 static int last_redraw_mask = REDRAW_NONE;
807 // force screen redraw in every frame to continue drawing global animations
808 // (but always use the last redraw mask to prevent unwanted side effects)
809 if (redraw_mask == REDRAW_NONE)
810 redraw_mask = last_redraw_mask;
812 last_redraw_mask = redraw_mask;
815 // masked border now drawn immediately when blitting backbuffer to window
817 // draw masked border to all viewports, if defined
818 DrawMaskedBorder(redraw_mask);
821 // draw frames per second (only if debug mode is enabled)
822 if (redraw_mask & REDRAW_FPS)
823 DrawFramesPerSecond();
825 // remove playfield redraw before potentially merging with doors redraw
826 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
827 redraw_mask &= ~REDRAW_FIELD;
829 // redraw complete window if both playfield and (some) doors need redraw
830 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
831 redraw_mask = REDRAW_ALL;
833 /* although redrawing the whole window would be fine for normal gameplay,
834 being able to only redraw the playfield is required for deactivating
835 certain drawing areas (mainly playfield) to work, which is needed for
836 warp-forward to be fast enough (by skipping redraw of most frames) */
838 if (redraw_mask & REDRAW_ALL)
840 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
842 else if (redraw_mask & REDRAW_FIELD)
844 BlitBitmap(backbuffer, window,
845 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
847 else if (redraw_mask & REDRAW_DOORS)
849 // merge door areas to prevent calling screen redraw more than once
855 if (redraw_mask & REDRAW_DOOR_1)
859 x2 = MAX(x2, DX + DXSIZE);
860 y2 = MAX(y2, DY + DYSIZE);
863 if (redraw_mask & REDRAW_DOOR_2)
867 x2 = MAX(x2, VX + VXSIZE);
868 y2 = MAX(y2, VY + VYSIZE);
871 if (redraw_mask & REDRAW_DOOR_3)
875 x2 = MAX(x2, EX + EXSIZE);
876 y2 = MAX(y2, EY + EYSIZE);
879 // make sure that at least one pixel is blitted, and inside the screen
880 // (else nothing is blitted, causing the animations not to be updated)
881 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
882 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
883 x2 = MIN(MAX(1, x2), WIN_XSIZE);
884 y2 = MIN(MAX(1, y2), WIN_YSIZE);
886 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
889 redraw_mask = REDRAW_NONE;
892 PrintFrameTimeDebugging();
896 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
898 unsigned int frame_delay_value_old = GetVideoFrameDelay();
900 SetVideoFrameDelay(frame_delay_value);
904 SetVideoFrameDelay(frame_delay_value_old);
907 static int fade_type_skip = FADE_TYPE_NONE;
909 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
911 void (*draw_border_function)(void) = NULL;
912 int x, y, width, height;
913 int fade_delay, post_delay;
915 if (fade_type == FADE_TYPE_FADE_OUT)
917 if (fade_type_skip != FADE_TYPE_NONE)
919 // skip all fade operations until specified fade operation
920 if (fade_type & fade_type_skip)
921 fade_type_skip = FADE_TYPE_NONE;
926 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
930 redraw_mask |= fade_mask;
932 if (fade_type == FADE_TYPE_SKIP)
934 fade_type_skip = fade_mode;
939 fade_delay = fading.fade_delay;
940 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
942 if (fade_type_skip != FADE_TYPE_NONE)
944 // skip all fade operations until specified fade operation
945 if (fade_type & fade_type_skip)
946 fade_type_skip = FADE_TYPE_NONE;
951 if (global.autoplay_leveldir)
956 if (fade_mask == REDRAW_FIELD)
961 height = FADE_SYSIZE;
963 if (border.draw_masked_when_fading)
964 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
966 DrawMaskedBorder_FIELD(); // draw once
976 if (!setup.fade_screens ||
978 fading.fade_mode == FADE_MODE_NONE)
980 if (fade_mode == FADE_MODE_FADE_OUT)
983 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
985 redraw_mask &= ~fade_mask;
990 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
991 draw_border_function);
993 redraw_mask &= ~fade_mask;
995 ClearAutoRepeatKeyEvents();
998 static void SetScreenStates_BeforeFadingIn(void)
1000 // temporarily set screen mode for animations to screen after fading in
1001 global.anim_status = global.anim_status_next;
1003 // store backbuffer with all animations that will be started after fading in
1004 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
1005 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
1007 // set screen mode for animations back to fading
1008 global.anim_status = GAME_MODE_PSEUDO_FADING;
1011 static void SetScreenStates_AfterFadingIn(void)
1013 // store new source screen (to use correct masked border for fading)
1014 gfx.fade_border_source_status = global.border_status;
1016 global.anim_status = global.anim_status_next;
1019 static void SetScreenStates_BeforeFadingOut(void)
1021 // store new target screen (to use correct masked border for fading)
1022 gfx.fade_border_target_status = game_status;
1024 // set screen mode for animations to fading
1025 global.anim_status = GAME_MODE_PSEUDO_FADING;
1027 // store backbuffer with all animations that will be stopped for fading out
1028 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1029 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1032 static void SetScreenStates_AfterFadingOut(void)
1034 global.border_status = game_status;
1037 void FadeIn(int fade_mask)
1039 SetScreenStates_BeforeFadingIn();
1042 DrawMaskedBorder(REDRAW_ALL);
1045 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1046 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1048 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1052 FADE_SXSIZE = FULL_SXSIZE;
1053 FADE_SYSIZE = FULL_SYSIZE;
1055 // activate virtual buttons depending on upcoming game status
1056 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1057 game_status == GAME_MODE_PLAYING && !tape.playing)
1058 SetOverlayActive(TRUE);
1060 SetScreenStates_AfterFadingIn();
1062 // force update of global animation status in case of rapid screen changes
1063 redraw_mask = REDRAW_ALL;
1067 void FadeOut(int fade_mask)
1069 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1070 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1071 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1074 SetScreenStates_BeforeFadingOut();
1076 SetTileCursorActive(FALSE);
1077 SetOverlayActive(FALSE);
1080 DrawMaskedBorder(REDRAW_ALL);
1083 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1084 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1086 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1088 SetScreenStates_AfterFadingOut();
1091 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1093 static struct TitleFadingInfo fading_leave_stored;
1096 fading_leave_stored = fading_leave;
1098 fading = fading_leave_stored;
1101 void FadeSetEnterMenu(void)
1103 fading = menu.enter_menu;
1105 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1108 void FadeSetLeaveMenu(void)
1110 fading = menu.leave_menu;
1112 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1115 void FadeSetEnterScreen(void)
1117 fading = menu.enter_screen[game_status];
1119 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1122 void FadeSetNextScreen(void)
1124 fading = menu.next_screen[game_status];
1126 // (do not overwrite fade mode set by FadeSetEnterScreen)
1127 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1130 void FadeSetLeaveScreen(void)
1132 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1135 void FadeSetFromType(int type)
1137 if (type & TYPE_ENTER_SCREEN)
1138 FadeSetEnterScreen();
1139 else if (type & TYPE_ENTER)
1141 else if (type & TYPE_LEAVE)
1145 void FadeSetDisabled(void)
1147 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1149 fading = fading_none;
1152 void FadeSkipNextFadeIn(void)
1154 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1157 void FadeSkipNextFadeOut(void)
1159 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1162 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1164 if (graphic == IMG_UNDEFINED)
1167 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1169 return (graphic_info[graphic].bitmap != NULL || redefined ?
1170 graphic_info[graphic].bitmap :
1171 graphic_info[default_graphic].bitmap);
1174 static Bitmap *getBackgroundBitmap(int graphic)
1176 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1179 static Bitmap *getGlobalBorderBitmap(int graphic)
1181 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1184 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1187 (status == GAME_MODE_MAIN ||
1188 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1189 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1190 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1191 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1194 return getGlobalBorderBitmap(graphic);
1197 void SetWindowBackgroundImageIfDefined(int graphic)
1199 if (graphic_info[graphic].bitmap)
1200 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1203 void SetMainBackgroundImageIfDefined(int graphic)
1205 if (graphic_info[graphic].bitmap)
1206 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1209 void SetDoorBackgroundImageIfDefined(int graphic)
1211 if (graphic_info[graphic].bitmap)
1212 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1215 void SetWindowBackgroundImage(int graphic)
1217 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1220 void SetMainBackgroundImage(int graphic)
1222 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1225 void SetDoorBackgroundImage(int graphic)
1227 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1230 void SetPanelBackground(void)
1232 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1234 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1235 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1237 SetDoorBackgroundBitmap(bitmap_db_panel);
1240 void DrawBackground(int x, int y, int width, int height)
1242 // "drawto" might still point to playfield buffer here (hall of fame)
1243 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1245 if (IN_GFX_FIELD_FULL(x, y))
1246 redraw_mask |= REDRAW_FIELD;
1247 else if (IN_GFX_DOOR_1(x, y))
1248 redraw_mask |= REDRAW_DOOR_1;
1249 else if (IN_GFX_DOOR_2(x, y))
1250 redraw_mask |= REDRAW_DOOR_2;
1251 else if (IN_GFX_DOOR_3(x, y))
1252 redraw_mask |= REDRAW_DOOR_3;
1255 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1257 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1259 if (font->bitmap == NULL)
1262 DrawBackground(x, y, width, height);
1265 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1267 struct GraphicInfo *g = &graphic_info[graphic];
1269 if (g->bitmap == NULL)
1272 DrawBackground(x, y, width, height);
1275 static int game_status_last = -1;
1276 static Bitmap *global_border_bitmap_last = NULL;
1277 static Bitmap *global_border_bitmap = NULL;
1278 static int real_sx_last = -1, real_sy_last = -1;
1279 static int full_sxsize_last = -1, full_sysize_last = -1;
1280 static int dx_last = -1, dy_last = -1;
1281 static int dxsize_last = -1, dysize_last = -1;
1282 static int vx_last = -1, vy_last = -1;
1283 static int vxsize_last = -1, vysize_last = -1;
1284 static int ex_last = -1, ey_last = -1;
1285 static int exsize_last = -1, eysize_last = -1;
1287 boolean CheckIfGlobalBorderHasChanged(void)
1289 // if game status has not changed, global border has not changed either
1290 if (game_status == game_status_last)
1293 // determine and store new global border bitmap for current game status
1294 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1296 return (global_border_bitmap_last != global_border_bitmap);
1299 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1301 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1302 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1304 // if game status has not changed, nothing has to be redrawn
1305 if (game_status == game_status_last)
1308 // redraw if last screen was title screen
1309 if (game_status_last == GAME_MODE_TITLE)
1312 // redraw if global screen border has changed
1313 if (CheckIfGlobalBorderHasChanged())
1316 // redraw if position or size of playfield area has changed
1317 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1318 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1321 // redraw if position or size of door area has changed
1322 if (dx_last != DX || dy_last != DY ||
1323 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1326 // redraw if position or size of tape area has changed
1327 if (vx_last != VX || vy_last != VY ||
1328 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1331 // redraw if position or size of editor area has changed
1332 if (ex_last != EX || ey_last != EY ||
1333 exsize_last != EXSIZE || eysize_last != EYSIZE)
1340 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1343 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1345 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1348 void RedrawGlobalBorder(void)
1350 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1352 RedrawGlobalBorderFromBitmap(bitmap);
1354 redraw_mask = REDRAW_ALL;
1357 static void RedrawGlobalBorderIfNeeded(void)
1359 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1360 if (game_status == game_status_last)
1364 // copy current draw buffer to later copy back areas that have not changed
1365 if (game_status_last != GAME_MODE_TITLE)
1366 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1368 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1369 if (CheckIfGlobalBorderRedrawIsNeeded())
1372 // redraw global screen border (or clear, if defined to be empty)
1373 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1375 if (game_status == GAME_MODE_EDITOR)
1376 DrawSpecialEditorDoor();
1378 // copy previous playfield and door areas, if they are defined on both
1379 // previous and current screen and if they still have the same size
1381 if (real_sx_last != -1 && real_sy_last != -1 &&
1382 REAL_SX != -1 && REAL_SY != -1 &&
1383 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1384 BlitBitmap(bitmap_db_store_1, backbuffer,
1385 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1388 if (dx_last != -1 && dy_last != -1 &&
1389 DX != -1 && DY != -1 &&
1390 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1391 BlitBitmap(bitmap_db_store_1, backbuffer,
1392 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1394 if (game_status != GAME_MODE_EDITOR)
1396 if (vx_last != -1 && vy_last != -1 &&
1397 VX != -1 && VY != -1 &&
1398 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1399 BlitBitmap(bitmap_db_store_1, backbuffer,
1400 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1404 if (ex_last != -1 && ey_last != -1 &&
1405 EX != -1 && EY != -1 &&
1406 exsize_last == EXSIZE && eysize_last == EYSIZE)
1407 BlitBitmap(bitmap_db_store_1, backbuffer,
1408 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1411 redraw_mask = REDRAW_ALL;
1414 game_status_last = game_status;
1416 global_border_bitmap_last = global_border_bitmap;
1418 real_sx_last = REAL_SX;
1419 real_sy_last = REAL_SY;
1420 full_sxsize_last = FULL_SXSIZE;
1421 full_sysize_last = FULL_SYSIZE;
1424 dxsize_last = DXSIZE;
1425 dysize_last = DYSIZE;
1428 vxsize_last = VXSIZE;
1429 vysize_last = VYSIZE;
1432 exsize_last = EXSIZE;
1433 eysize_last = EYSIZE;
1436 void ClearField(void)
1438 RedrawGlobalBorderIfNeeded();
1440 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1441 // (when entering hall of fame after playing)
1442 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1444 // !!! maybe this should be done before clearing the background !!!
1445 if (game_status == GAME_MODE_PLAYING)
1447 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1448 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1452 SetDrawtoField(DRAW_TO_BACKBUFFER);
1456 void MarkTileDirty(int x, int y)
1458 redraw_mask |= REDRAW_FIELD;
1461 void SetBorderElement(void)
1465 BorderElement = EL_EMPTY;
1467 // the MM game engine does not use a visible border element
1468 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1471 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1473 for (x = 0; x < lev_fieldx; x++)
1475 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1476 BorderElement = EL_STEELWALL;
1478 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1484 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1485 int max_array_fieldx, int max_array_fieldy,
1486 short field[max_array_fieldx][max_array_fieldy],
1487 int max_fieldx, int max_fieldy)
1491 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1492 static int safety = 0;
1494 // check if starting field still has the desired content
1495 if (field[from_x][from_y] == fill_element)
1500 if (safety > max_fieldx * max_fieldy)
1501 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1503 old_element = field[from_x][from_y];
1504 field[from_x][from_y] = fill_element;
1506 for (i = 0; i < 4; i++)
1508 x = from_x + check[i][0];
1509 y = from_y + check[i][1];
1511 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1512 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1513 field, max_fieldx, max_fieldy);
1519 void FloodFillLevel(int from_x, int from_y, int fill_element,
1520 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1521 int max_fieldx, int max_fieldy)
1523 FloodFillLevelExt(from_x, from_y, fill_element,
1524 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1525 max_fieldx, max_fieldy);
1528 void SetRandomAnimationValue(int x, int y)
1530 gfx.anim_random_frame = GfxRandom[x][y];
1533 int getGraphicAnimationFrame(int graphic, int sync_frame)
1535 // animation synchronized with global frame counter, not move position
1536 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1537 sync_frame = FrameCounter;
1539 return getAnimationFrame(graphic_info[graphic].anim_frames,
1540 graphic_info[graphic].anim_delay,
1541 graphic_info[graphic].anim_mode,
1542 graphic_info[graphic].anim_start_frame,
1546 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1548 struct GraphicInfo *g = &graphic_info[graphic];
1549 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1551 if (tilesize == gfx.standard_tile_size)
1552 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1553 else if (tilesize == game.tile_size)
1554 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1556 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1559 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1560 boolean get_backside)
1562 struct GraphicInfo *g = &graphic_info[graphic];
1563 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1564 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1566 if (g->offset_y == 0) // frames are ordered horizontally
1568 int max_width = g->anim_frames_per_line * g->width;
1569 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1571 *x = pos % max_width;
1572 *y = src_y % g->height + pos / max_width * g->height;
1574 else if (g->offset_x == 0) // frames are ordered vertically
1576 int max_height = g->anim_frames_per_line * g->height;
1577 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1579 *x = src_x % g->width + pos / max_height * g->width;
1580 *y = pos % max_height;
1582 else // frames are ordered diagonally
1584 *x = src_x + frame * g->offset_x;
1585 *y = src_y + frame * g->offset_y;
1589 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1590 Bitmap **bitmap, int *x, int *y,
1591 boolean get_backside)
1593 struct GraphicInfo *g = &graphic_info[graphic];
1595 // if no graphics defined at all, use fallback graphics
1596 if (g->bitmaps == NULL)
1597 *g = graphic_info[IMG_CHAR_EXCLAM];
1599 // if no in-game graphics defined, always use standard graphic size
1600 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1601 tilesize = TILESIZE;
1603 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1604 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1606 *x = *x * tilesize / g->tile_size;
1607 *y = *y * tilesize / g->tile_size;
1610 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1611 Bitmap **bitmap, int *x, int *y)
1613 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1616 void getFixedGraphicSource(int graphic, int frame,
1617 Bitmap **bitmap, int *x, int *y)
1619 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1622 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1624 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1627 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1628 int *x, int *y, boolean get_backside)
1630 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1634 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1636 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1639 void DrawGraphic(int x, int y, int graphic, int frame)
1642 if (!IN_SCR_FIELD(x, y))
1644 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1645 printf("DrawGraphic(): This should never happen!\n");
1650 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1653 MarkTileDirty(x, y);
1656 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1659 if (!IN_SCR_FIELD(x, y))
1661 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1662 printf("DrawGraphic(): This should never happen!\n");
1667 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1669 MarkTileDirty(x, y);
1672 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1678 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1680 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1683 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1689 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1690 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1693 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1696 if (!IN_SCR_FIELD(x, y))
1698 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1699 printf("DrawGraphicThruMask(): This should never happen!\n");
1704 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1707 MarkTileDirty(x, y);
1710 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1713 if (!IN_SCR_FIELD(x, y))
1715 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1716 printf("DrawGraphicThruMask(): This should never happen!\n");
1721 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1723 MarkTileDirty(x, y);
1726 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1732 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1734 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1738 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1739 int graphic, int frame)
1744 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1746 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1750 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1752 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1754 MarkTileDirty(x / tilesize, y / tilesize);
1757 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1760 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1761 graphic, frame, tilesize);
1762 MarkTileDirty(x / tilesize, y / tilesize);
1765 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1771 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1772 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1775 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1776 int frame, int tilesize)
1781 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1782 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1785 void DrawMiniGraphic(int x, int y, int graphic)
1787 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1788 MarkTileDirty(x / 2, y / 2);
1791 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1796 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1797 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1800 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1801 int graphic, int frame,
1802 int cut_mode, int mask_mode)
1807 int width = TILEX, height = TILEY;
1810 if (dx || dy) // shifted graphic
1812 if (x < BX1) // object enters playfield from the left
1819 else if (x > BX2) // object enters playfield from the right
1825 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1831 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1833 else if (dx) // general horizontal movement
1834 MarkTileDirty(x + SIGN(dx), y);
1836 if (y < BY1) // object enters playfield from the top
1838 if (cut_mode == CUT_BELOW) // object completely above top border
1846 else if (y > BY2) // object enters playfield from the bottom
1852 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1858 else if (dy > 0 && cut_mode == CUT_ABOVE)
1860 if (y == BY2) // object completely above bottom border
1866 MarkTileDirty(x, y + 1);
1867 } // object leaves playfield to the bottom
1868 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1870 else if (dy) // general vertical movement
1871 MarkTileDirty(x, y + SIGN(dy));
1875 if (!IN_SCR_FIELD(x, y))
1877 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1878 printf("DrawGraphicShifted(): This should never happen!\n");
1883 width = width * TILESIZE_VAR / TILESIZE;
1884 height = height * TILESIZE_VAR / TILESIZE;
1885 cx = cx * TILESIZE_VAR / TILESIZE;
1886 cy = cy * TILESIZE_VAR / TILESIZE;
1887 dx = dx * TILESIZE_VAR / TILESIZE;
1888 dy = dy * TILESIZE_VAR / TILESIZE;
1890 if (width > 0 && height > 0)
1892 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1897 dst_x = FX + x * TILEX_VAR + dx;
1898 dst_y = FY + y * TILEY_VAR + dy;
1900 if (mask_mode == USE_MASKING)
1901 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1904 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1907 MarkTileDirty(x, y);
1911 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1912 int graphic, int frame,
1913 int cut_mode, int mask_mode)
1918 int width = TILEX_VAR, height = TILEY_VAR;
1921 int x2 = x + SIGN(dx);
1922 int y2 = y + SIGN(dy);
1924 // movement with two-tile animations must be sync'ed with movement position,
1925 // not with current GfxFrame (which can be higher when using slow movement)
1926 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1927 int anim_frames = graphic_info[graphic].anim_frames;
1929 // (we also need anim_delay here for movement animations with less frames)
1930 int anim_delay = graphic_info[graphic].anim_delay;
1931 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1933 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1934 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1936 // re-calculate animation frame for two-tile movement animation
1937 frame = getGraphicAnimationFrame(graphic, sync_frame);
1939 // check if movement start graphic inside screen area and should be drawn
1940 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1942 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1944 dst_x = FX + x1 * TILEX_VAR;
1945 dst_y = FY + y1 * 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(x1, y1);
1957 // check if movement end graphic inside screen area and should be drawn
1958 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1960 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1962 dst_x = FX + x2 * TILEX_VAR;
1963 dst_y = FY + y2 * TILEY_VAR;
1965 if (mask_mode == USE_MASKING)
1966 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1969 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1972 MarkTileDirty(x2, y2);
1976 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1977 int graphic, int frame,
1978 int cut_mode, int mask_mode)
1982 DrawGraphic(x, y, graphic, frame);
1987 if (graphic_info[graphic].double_movement) // EM style movement images
1988 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1990 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1993 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1994 int graphic, int frame, int cut_mode)
1996 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1999 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
2000 int cut_mode, int mask_mode)
2002 int lx = LEVELX(x), ly = LEVELY(y);
2006 if (IN_LEV_FIELD(lx, ly))
2008 SetRandomAnimationValue(lx, ly);
2010 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2011 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2013 // do not use double (EM style) movement graphic when not moving
2014 if (graphic_info[graphic].double_movement && !dx && !dy)
2016 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2017 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2020 else // border element
2022 graphic = el2img(element);
2023 frame = getGraphicAnimationFrame(graphic, -1);
2026 if (element == EL_EXPANDABLE_WALL)
2028 boolean left_stopped = FALSE, right_stopped = FALSE;
2030 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2031 left_stopped = TRUE;
2032 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2033 right_stopped = TRUE;
2035 if (left_stopped && right_stopped)
2037 else if (left_stopped)
2039 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2040 frame = graphic_info[graphic].anim_frames - 1;
2042 else if (right_stopped)
2044 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2045 frame = graphic_info[graphic].anim_frames - 1;
2050 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2051 else if (mask_mode == USE_MASKING)
2052 DrawGraphicThruMask(x, y, graphic, frame);
2054 DrawGraphic(x, y, graphic, frame);
2057 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2058 int cut_mode, int mask_mode)
2060 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2061 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2062 cut_mode, mask_mode);
2065 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2068 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2071 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2074 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2077 void DrawLevelElementThruMask(int x, int y, int element)
2079 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2082 void DrawLevelFieldThruMask(int x, int y)
2084 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2087 // !!! implementation of quicksand is totally broken !!!
2088 #define IS_CRUMBLED_TILE(x, y, e) \
2089 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2090 !IS_MOVING(x, y) || \
2091 (e) == EL_QUICKSAND_EMPTYING || \
2092 (e) == EL_QUICKSAND_FAST_EMPTYING))
2094 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2099 int width, height, cx, cy;
2100 int sx = SCREENX(x), sy = SCREENY(y);
2101 int crumbled_border_size = graphic_info[graphic].border_size;
2102 int crumbled_tile_size = graphic_info[graphic].tile_size;
2103 int crumbled_border_size_var =
2104 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2107 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2109 for (i = 1; i < 4; i++)
2111 int dxx = (i & 1 ? dx : 0);
2112 int dyy = (i & 2 ? dy : 0);
2115 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2118 // check if neighbour field is of same crumble type
2119 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2120 graphic_info[graphic].class ==
2121 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2123 // return if check prevents inner corner
2124 if (same == (dxx == dx && dyy == dy))
2128 // if we reach this point, we have an inner corner
2130 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2132 width = crumbled_border_size_var;
2133 height = crumbled_border_size_var;
2134 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2135 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2137 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2138 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2141 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2146 int width, height, bx, by, cx, cy;
2147 int sx = SCREENX(x), sy = SCREENY(y);
2148 int crumbled_border_size = graphic_info[graphic].border_size;
2149 int crumbled_tile_size = graphic_info[graphic].tile_size;
2150 int crumbled_border_size_var =
2151 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2152 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2155 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2157 // draw simple, sloppy, non-corner-accurate crumbled border
2159 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2160 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2161 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2162 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2164 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2165 FX + sx * TILEX_VAR + cx,
2166 FY + sy * TILEY_VAR + cy);
2168 // (remaining middle border part must be at least as big as corner part)
2169 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2170 crumbled_border_size_var >= TILESIZE_VAR / 3)
2173 // correct corners of crumbled border, if needed
2175 for (i = -1; i <= 1; i += 2)
2177 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2178 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2179 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2182 // check if neighbour field is of same crumble type
2183 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2184 graphic_info[graphic].class ==
2185 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2187 // no crumbled corner, but continued crumbled border
2189 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2190 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2191 int b1 = (i == 1 ? crumbled_border_size_var :
2192 TILESIZE_VAR - 2 * crumbled_border_size_var);
2194 width = crumbled_border_size_var;
2195 height = crumbled_border_size_var;
2197 if (dir == 1 || dir == 2)
2212 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2214 FX + sx * TILEX_VAR + cx,
2215 FY + sy * TILEY_VAR + cy);
2220 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2222 int sx = SCREENX(x), sy = SCREENY(y);
2225 static int xy[4][2] =
2233 if (!IN_LEV_FIELD(x, y))
2236 element = TILE_GFX_ELEMENT(x, y);
2238 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2240 if (!IN_SCR_FIELD(sx, sy))
2243 // crumble field borders towards direct neighbour fields
2244 for (i = 0; i < 4; i++)
2246 int xx = x + xy[i][0];
2247 int yy = y + xy[i][1];
2249 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2252 // check if neighbour field is of same crumble type
2253 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2254 graphic_info[graphic].class ==
2255 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2258 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2261 // crumble inner field corners towards corner neighbour fields
2262 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2263 graphic_info[graphic].anim_frames == 2)
2265 for (i = 0; i < 4; i++)
2267 int dx = (i & 1 ? +1 : -1);
2268 int dy = (i & 2 ? +1 : -1);
2270 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2274 MarkTileDirty(sx, sy);
2276 else // center field is not crumbled -- crumble neighbour fields
2278 // crumble field borders of direct neighbour fields
2279 for (i = 0; i < 4; i++)
2281 int xx = x + xy[i][0];
2282 int yy = y + xy[i][1];
2283 int sxx = sx + xy[i][0];
2284 int syy = sy + xy[i][1];
2286 if (!IN_LEV_FIELD(xx, yy) ||
2287 !IN_SCR_FIELD(sxx, syy))
2290 // do not crumble fields that are being digged or snapped
2291 if (Feld[xx][yy] == EL_EMPTY ||
2292 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2295 element = TILE_GFX_ELEMENT(xx, yy);
2297 if (!IS_CRUMBLED_TILE(xx, yy, element))
2300 graphic = el_act2crm(element, ACTION_DEFAULT);
2302 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2304 MarkTileDirty(sxx, syy);
2307 // crumble inner field corners of corner neighbour fields
2308 for (i = 0; i < 4; i++)
2310 int dx = (i & 1 ? +1 : -1);
2311 int dy = (i & 2 ? +1 : -1);
2317 if (!IN_LEV_FIELD(xx, yy) ||
2318 !IN_SCR_FIELD(sxx, syy))
2321 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2324 element = TILE_GFX_ELEMENT(xx, yy);
2326 if (!IS_CRUMBLED_TILE(xx, yy, element))
2329 graphic = el_act2crm(element, ACTION_DEFAULT);
2331 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2332 graphic_info[graphic].anim_frames == 2)
2333 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2335 MarkTileDirty(sxx, syy);
2340 void DrawLevelFieldCrumbled(int x, int y)
2344 if (!IN_LEV_FIELD(x, y))
2347 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2348 GfxElement[x][y] != EL_UNDEFINED &&
2349 GFX_CRUMBLED(GfxElement[x][y]))
2351 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2356 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2358 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2361 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2364 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2365 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2366 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2367 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2368 int sx = SCREENX(x), sy = SCREENY(y);
2370 DrawGraphic(sx, sy, graphic1, frame1);
2371 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2374 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2376 int sx = SCREENX(x), sy = SCREENY(y);
2377 static int xy[4][2] =
2386 // crumble direct neighbour fields (required for field borders)
2387 for (i = 0; i < 4; i++)
2389 int xx = x + xy[i][0];
2390 int yy = y + xy[i][1];
2391 int sxx = sx + xy[i][0];
2392 int syy = sy + xy[i][1];
2394 if (!IN_LEV_FIELD(xx, yy) ||
2395 !IN_SCR_FIELD(sxx, syy) ||
2396 !GFX_CRUMBLED(Feld[xx][yy]) ||
2400 DrawLevelField(xx, yy);
2403 // crumble corner neighbour fields (required for inner field corners)
2404 for (i = 0; i < 4; i++)
2406 int dx = (i & 1 ? +1 : -1);
2407 int dy = (i & 2 ? +1 : -1);
2413 if (!IN_LEV_FIELD(xx, yy) ||
2414 !IN_SCR_FIELD(sxx, syy) ||
2415 !GFX_CRUMBLED(Feld[xx][yy]) ||
2419 int element = TILE_GFX_ELEMENT(xx, yy);
2420 int graphic = el_act2crm(element, ACTION_DEFAULT);
2422 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2423 graphic_info[graphic].anim_frames == 2)
2424 DrawLevelField(xx, yy);
2428 static int getBorderElement(int x, int y)
2432 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2433 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2434 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2435 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2436 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2437 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2438 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2440 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2441 int steel_position = (x == -1 && y == -1 ? 0 :
2442 x == lev_fieldx && y == -1 ? 1 :
2443 x == -1 && y == lev_fieldy ? 2 :
2444 x == lev_fieldx && y == lev_fieldy ? 3 :
2445 x == -1 || x == lev_fieldx ? 4 :
2446 y == -1 || y == lev_fieldy ? 5 : 6);
2448 return border[steel_position][steel_type];
2451 void DrawScreenElement(int x, int y, int element)
2453 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2454 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2457 void DrawLevelElement(int x, int y, int element)
2459 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2460 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2463 void DrawScreenField(int x, int y)
2465 int lx = LEVELX(x), ly = LEVELY(y);
2466 int element, content;
2468 if (!IN_LEV_FIELD(lx, ly))
2470 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2473 element = getBorderElement(lx, ly);
2475 DrawScreenElement(x, y, element);
2480 element = Feld[lx][ly];
2481 content = Store[lx][ly];
2483 if (IS_MOVING(lx, ly))
2485 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2486 boolean cut_mode = NO_CUTTING;
2488 if (element == EL_QUICKSAND_EMPTYING ||
2489 element == EL_QUICKSAND_FAST_EMPTYING ||
2490 element == EL_MAGIC_WALL_EMPTYING ||
2491 element == EL_BD_MAGIC_WALL_EMPTYING ||
2492 element == EL_DC_MAGIC_WALL_EMPTYING ||
2493 element == EL_AMOEBA_DROPPING)
2494 cut_mode = CUT_ABOVE;
2495 else if (element == EL_QUICKSAND_FILLING ||
2496 element == EL_QUICKSAND_FAST_FILLING ||
2497 element == EL_MAGIC_WALL_FILLING ||
2498 element == EL_BD_MAGIC_WALL_FILLING ||
2499 element == EL_DC_MAGIC_WALL_FILLING)
2500 cut_mode = CUT_BELOW;
2502 if (cut_mode == CUT_ABOVE)
2503 DrawScreenElement(x, y, element);
2505 DrawScreenElement(x, y, EL_EMPTY);
2508 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2509 else if (cut_mode == NO_CUTTING)
2510 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2513 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2515 if (cut_mode == CUT_BELOW &&
2516 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2517 DrawLevelElement(lx, ly + 1, element);
2520 if (content == EL_ACID)
2522 int dir = MovDir[lx][ly];
2523 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2524 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2526 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2528 // prevent target field from being drawn again (but without masking)
2529 // (this would happen if target field is scanned after moving element)
2530 Stop[newlx][newly] = TRUE;
2533 else if (IS_BLOCKED(lx, ly))
2538 boolean cut_mode = NO_CUTTING;
2539 int element_old, content_old;
2541 Blocked2Moving(lx, ly, &oldx, &oldy);
2544 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2545 MovDir[oldx][oldy] == MV_RIGHT);
2547 element_old = Feld[oldx][oldy];
2548 content_old = Store[oldx][oldy];
2550 if (element_old == EL_QUICKSAND_EMPTYING ||
2551 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2552 element_old == EL_MAGIC_WALL_EMPTYING ||
2553 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2554 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2555 element_old == EL_AMOEBA_DROPPING)
2556 cut_mode = CUT_ABOVE;
2558 DrawScreenElement(x, y, EL_EMPTY);
2561 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2563 else if (cut_mode == NO_CUTTING)
2564 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2567 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2570 else if (IS_DRAWABLE(element))
2571 DrawScreenElement(x, y, element);
2573 DrawScreenElement(x, y, EL_EMPTY);
2576 void DrawLevelField(int x, int y)
2578 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2579 DrawScreenField(SCREENX(x), SCREENY(y));
2580 else if (IS_MOVING(x, y))
2584 Moving2Blocked(x, y, &newx, &newy);
2585 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2586 DrawScreenField(SCREENX(newx), SCREENY(newy));
2588 else if (IS_BLOCKED(x, y))
2592 Blocked2Moving(x, y, &oldx, &oldy);
2593 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2594 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2598 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2599 int (*el2img_function)(int), boolean masked,
2600 int element_bits_draw)
2602 int element_base = map_mm_wall_element(element);
2603 int element_bits = (IS_DF_WALL(element) ?
2604 element - EL_DF_WALL_START :
2605 IS_MM_WALL(element) ?
2606 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2607 int graphic = el2img_function(element_base);
2608 int tilesize_draw = tilesize / 2;
2613 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2615 for (i = 0; i < 4; i++)
2617 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2618 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2620 if (!(element_bits_draw & (1 << i)))
2623 if (element_bits & (1 << i))
2626 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2627 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2629 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2630 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2635 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2636 tilesize_draw, tilesize_draw);
2641 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2642 boolean masked, int element_bits_draw)
2644 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2645 element, tilesize, el2edimg, masked, element_bits_draw);
2648 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2649 int (*el2img_function)(int))
2651 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2655 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2658 if (IS_MM_WALL(element))
2660 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2661 element, tilesize, el2edimg, masked, 0x000f);
2665 int graphic = el2edimg(element);
2668 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2670 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2674 void DrawSizedElement(int x, int y, int element, int tilesize)
2676 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2679 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2681 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2684 void DrawMiniElement(int x, int y, int element)
2688 graphic = el2edimg(element);
2689 DrawMiniGraphic(x, y, graphic);
2692 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2695 int x = sx + scroll_x, y = sy + scroll_y;
2697 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2698 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2699 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2700 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2702 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2705 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2707 int x = sx + scroll_x, y = sy + scroll_y;
2709 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2710 DrawMiniElement(sx, sy, EL_EMPTY);
2711 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2712 DrawMiniElement(sx, sy, Feld[x][y]);
2714 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2717 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2718 int x, int y, int xsize, int ysize,
2719 int tile_width, int tile_height)
2723 int dst_x = startx + x * tile_width;
2724 int dst_y = starty + y * tile_height;
2725 int width = graphic_info[graphic].width;
2726 int height = graphic_info[graphic].height;
2727 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2728 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2729 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2730 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2731 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2732 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2733 boolean draw_masked = graphic_info[graphic].draw_masked;
2735 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2737 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2739 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2743 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2744 inner_sx + (x - 1) * tile_width % inner_width);
2745 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2746 inner_sy + (y - 1) * tile_height % inner_height);
2749 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2752 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2756 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2757 int x, int y, int xsize, int ysize,
2760 int font_width = getFontWidth(font_nr);
2761 int font_height = getFontHeight(font_nr);
2763 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2764 font_width, font_height);
2767 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2769 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2770 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2771 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2772 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2773 boolean no_delay = (tape.warp_forward);
2774 unsigned int anim_delay = 0;
2775 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2776 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2777 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2778 int font_width = getFontWidth(font_nr);
2779 int font_height = getFontHeight(font_nr);
2780 int max_xsize = level.envelope[envelope_nr].xsize;
2781 int max_ysize = level.envelope[envelope_nr].ysize;
2782 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2783 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2784 int xend = max_xsize;
2785 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2786 int xstep = (xstart < xend ? 1 : 0);
2787 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2789 int end = MAX(xend - xstart, yend - ystart);
2792 for (i = start; i <= end; i++)
2794 int last_frame = end; // last frame of this "for" loop
2795 int x = xstart + i * xstep;
2796 int y = ystart + i * ystep;
2797 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2798 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2799 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2800 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2803 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2805 BlitScreenToBitmap(backbuffer);
2807 SetDrawtoField(DRAW_TO_BACKBUFFER);
2809 for (yy = 0; yy < ysize; yy++)
2810 for (xx = 0; xx < xsize; xx++)
2811 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2813 DrawTextBuffer(sx + font_width, sy + font_height,
2814 level.envelope[envelope_nr].text, font_nr, max_xsize,
2815 xsize - 2, ysize - 2, 0, mask_mode,
2816 level.envelope[envelope_nr].autowrap,
2817 level.envelope[envelope_nr].centered, FALSE);
2819 redraw_mask |= REDRAW_FIELD;
2822 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2825 ClearAutoRepeatKeyEvents();
2828 void ShowEnvelope(int envelope_nr)
2830 int element = EL_ENVELOPE_1 + envelope_nr;
2831 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2832 int sound_opening = element_info[element].sound[ACTION_OPENING];
2833 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2834 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2835 boolean no_delay = (tape.warp_forward);
2836 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2837 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2838 int anim_mode = graphic_info[graphic].anim_mode;
2839 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2840 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2842 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2844 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2846 if (anim_mode == ANIM_DEFAULT)
2847 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2849 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2852 Delay_WithScreenUpdates(wait_delay_value);
2854 WaitForEventToContinue();
2856 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2858 if (anim_mode != ANIM_NONE)
2859 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2861 if (anim_mode == ANIM_DEFAULT)
2862 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2864 game.envelope_active = FALSE;
2866 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2868 redraw_mask |= REDRAW_FIELD;
2872 static void setRequestBasePosition(int *x, int *y)
2874 int sx_base, sy_base;
2876 if (request.x != -1)
2877 sx_base = request.x;
2878 else if (request.align == ALIGN_LEFT)
2880 else if (request.align == ALIGN_RIGHT)
2881 sx_base = SX + SXSIZE;
2883 sx_base = SX + SXSIZE / 2;
2885 if (request.y != -1)
2886 sy_base = request.y;
2887 else if (request.valign == VALIGN_TOP)
2889 else if (request.valign == VALIGN_BOTTOM)
2890 sy_base = SY + SYSIZE;
2892 sy_base = SY + SYSIZE / 2;
2898 static void setRequestPositionExt(int *x, int *y, int width, int height,
2899 boolean add_border_size)
2901 int border_size = request.border_size;
2902 int sx_base, sy_base;
2905 setRequestBasePosition(&sx_base, &sy_base);
2907 if (request.align == ALIGN_LEFT)
2909 else if (request.align == ALIGN_RIGHT)
2910 sx = sx_base - width;
2912 sx = sx_base - width / 2;
2914 if (request.valign == VALIGN_TOP)
2916 else if (request.valign == VALIGN_BOTTOM)
2917 sy = sy_base - height;
2919 sy = sy_base - height / 2;
2921 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2922 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2924 if (add_border_size)
2934 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2936 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2939 static void DrawEnvelopeRequest(char *text)
2941 char *text_final = text;
2942 char *text_door_style = NULL;
2943 int graphic = IMG_BACKGROUND_REQUEST;
2944 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2945 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2946 int font_nr = FONT_REQUEST;
2947 int font_width = getFontWidth(font_nr);
2948 int font_height = getFontHeight(font_nr);
2949 int border_size = request.border_size;
2950 int line_spacing = request.line_spacing;
2951 int line_height = font_height + line_spacing;
2952 int max_text_width = request.width - 2 * border_size;
2953 int max_text_height = request.height - 2 * border_size;
2954 int line_length = max_text_width / font_width;
2955 int max_lines = max_text_height / line_height;
2956 int text_width = line_length * font_width;
2957 int width = request.width;
2958 int height = request.height;
2959 int tile_size = MAX(request.step_offset, 1);
2960 int x_steps = width / tile_size;
2961 int y_steps = height / tile_size;
2962 int sx_offset = border_size;
2963 int sy_offset = border_size;
2967 if (request.centered)
2968 sx_offset = (request.width - text_width) / 2;
2970 if (request.wrap_single_words && !request.autowrap)
2972 char *src_text_ptr, *dst_text_ptr;
2974 text_door_style = checked_malloc(2 * strlen(text) + 1);
2976 src_text_ptr = text;
2977 dst_text_ptr = text_door_style;
2979 while (*src_text_ptr)
2981 if (*src_text_ptr == ' ' ||
2982 *src_text_ptr == '?' ||
2983 *src_text_ptr == '!')
2984 *dst_text_ptr++ = '\n';
2986 if (*src_text_ptr != ' ')
2987 *dst_text_ptr++ = *src_text_ptr;
2992 *dst_text_ptr = '\0';
2994 text_final = text_door_style;
2997 setRequestPosition(&sx, &sy, FALSE);
2999 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
3001 for (y = 0; y < y_steps; y++)
3002 for (x = 0; x < x_steps; x++)
3003 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3004 x, y, x_steps, y_steps,
3005 tile_size, tile_size);
3007 // force DOOR font inside door area
3008 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3010 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3011 line_length, -1, max_lines, line_spacing, mask_mode,
3012 request.autowrap, request.centered, FALSE);
3016 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3017 RedrawGadget(tool_gadget[i]);
3019 // store readily prepared envelope request for later use when animating
3020 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3022 if (text_door_style)
3023 free(text_door_style);
3026 static void AnimateEnvelopeRequest(int anim_mode, int action)
3028 int graphic = IMG_BACKGROUND_REQUEST;
3029 boolean draw_masked = graphic_info[graphic].draw_masked;
3030 int delay_value_normal = request.step_delay;
3031 int delay_value_fast = delay_value_normal / 2;
3032 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3033 boolean no_delay = (tape.warp_forward);
3034 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3035 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3036 unsigned int anim_delay = 0;
3038 int tile_size = MAX(request.step_offset, 1);
3039 int max_xsize = request.width / tile_size;
3040 int max_ysize = request.height / tile_size;
3041 int max_xsize_inner = max_xsize - 2;
3042 int max_ysize_inner = max_ysize - 2;
3044 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3045 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3046 int xend = max_xsize_inner;
3047 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3048 int xstep = (xstart < xend ? 1 : 0);
3049 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3051 int end = MAX(xend - xstart, yend - ystart);
3054 if (setup.quick_doors)
3061 for (i = start; i <= end; i++)
3063 int last_frame = end; // last frame of this "for" loop
3064 int x = xstart + i * xstep;
3065 int y = ystart + i * ystep;
3066 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3067 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3068 int xsize_size_left = (xsize - 1) * tile_size;
3069 int ysize_size_top = (ysize - 1) * tile_size;
3070 int max_xsize_pos = (max_xsize - 1) * tile_size;
3071 int max_ysize_pos = (max_ysize - 1) * tile_size;
3072 int width = xsize * tile_size;
3073 int height = ysize * tile_size;
3078 setRequestPosition(&src_x, &src_y, FALSE);
3079 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3081 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3083 for (yy = 0; yy < 2; yy++)
3085 for (xx = 0; xx < 2; xx++)
3087 int src_xx = src_x + xx * max_xsize_pos;
3088 int src_yy = src_y + yy * max_ysize_pos;
3089 int dst_xx = dst_x + xx * xsize_size_left;
3090 int dst_yy = dst_y + yy * ysize_size_top;
3091 int xx_size = (xx ? tile_size : xsize_size_left);
3092 int yy_size = (yy ? tile_size : ysize_size_top);
3095 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3096 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3098 BlitBitmap(bitmap_db_store_2, backbuffer,
3099 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3103 redraw_mask |= REDRAW_FIELD;
3107 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3110 ClearAutoRepeatKeyEvents();
3113 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3115 int graphic = IMG_BACKGROUND_REQUEST;
3116 int sound_opening = SND_REQUEST_OPENING;
3117 int sound_closing = SND_REQUEST_CLOSING;
3118 int anim_mode_1 = request.anim_mode; // (higher priority)
3119 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3120 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3121 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3122 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3124 if (game_status == GAME_MODE_PLAYING)
3125 BlitScreenToBitmap(backbuffer);
3127 SetDrawtoField(DRAW_TO_BACKBUFFER);
3129 // SetDrawBackgroundMask(REDRAW_NONE);
3131 if (action == ACTION_OPENING)
3133 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3135 if (req_state & REQ_ASK)
3137 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3138 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3139 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3140 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3142 else if (req_state & REQ_CONFIRM)
3144 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3145 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3147 else if (req_state & REQ_PLAYER)
3149 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3150 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3151 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3152 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3155 DrawEnvelopeRequest(text);
3158 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3160 if (action == ACTION_OPENING)
3162 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3164 if (anim_mode == ANIM_DEFAULT)
3165 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3167 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3171 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3173 if (anim_mode != ANIM_NONE)
3174 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3176 if (anim_mode == ANIM_DEFAULT)
3177 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3180 game.envelope_active = FALSE;
3182 if (action == ACTION_CLOSING)
3183 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3185 // SetDrawBackgroundMask(last_draw_background_mask);
3187 redraw_mask |= REDRAW_FIELD;
3191 if (action == ACTION_CLOSING &&
3192 game_status == GAME_MODE_PLAYING &&
3193 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3194 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3197 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3199 if (IS_MM_WALL(element))
3201 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3207 int graphic = el2preimg(element);
3209 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3210 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3215 void DrawLevel(int draw_background_mask)
3219 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3220 SetDrawBackgroundMask(draw_background_mask);
3224 for (x = BX1; x <= BX2; x++)
3225 for (y = BY1; y <= BY2; y++)
3226 DrawScreenField(x, y);
3228 redraw_mask |= REDRAW_FIELD;
3231 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3236 for (x = 0; x < size_x; x++)
3237 for (y = 0; y < size_y; y++)
3238 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3240 redraw_mask |= REDRAW_FIELD;
3243 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3247 for (x = 0; x < size_x; x++)
3248 for (y = 0; y < size_y; y++)
3249 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3251 redraw_mask |= REDRAW_FIELD;
3254 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3256 boolean show_level_border = (BorderElement != EL_EMPTY);
3257 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3258 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3259 int tile_size = preview.tile_size;
3260 int preview_width = preview.xsize * tile_size;
3261 int preview_height = preview.ysize * tile_size;
3262 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3263 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3264 int real_preview_width = real_preview_xsize * tile_size;
3265 int real_preview_height = real_preview_ysize * tile_size;
3266 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3267 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3270 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3273 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3275 dst_x += (preview_width - real_preview_width) / 2;
3276 dst_y += (preview_height - real_preview_height) / 2;
3278 for (x = 0; x < real_preview_xsize; x++)
3280 for (y = 0; y < real_preview_ysize; y++)
3282 int lx = from_x + x + (show_level_border ? -1 : 0);
3283 int ly = from_y + y + (show_level_border ? -1 : 0);
3284 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3285 getBorderElement(lx, ly));
3287 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3288 element, tile_size);
3292 redraw_mask |= REDRAW_FIELD;
3295 #define MICROLABEL_EMPTY 0
3296 #define MICROLABEL_LEVEL_NAME 1
3297 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3298 #define MICROLABEL_LEVEL_AUTHOR 3
3299 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3300 #define MICROLABEL_IMPORTED_FROM 5
3301 #define MICROLABEL_IMPORTED_BY_HEAD 6
3302 #define MICROLABEL_IMPORTED_BY 7
3304 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3306 int max_text_width = SXSIZE;
3307 int font_width = getFontWidth(font_nr);
3309 if (pos->align == ALIGN_CENTER)
3310 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3311 else if (pos->align == ALIGN_RIGHT)
3312 max_text_width = pos->x;
3314 max_text_width = SXSIZE - pos->x;
3316 return max_text_width / font_width;
3319 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3321 char label_text[MAX_OUTPUT_LINESIZE + 1];
3322 int max_len_label_text;
3323 int font_nr = pos->font;
3326 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3329 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3330 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3331 mode == MICROLABEL_IMPORTED_BY_HEAD)
3332 font_nr = pos->font_alt;
3334 max_len_label_text = getMaxTextLength(pos, font_nr);
3336 if (pos->size != -1)
3337 max_len_label_text = pos->size;
3339 for (i = 0; i < max_len_label_text; i++)
3340 label_text[i] = ' ';
3341 label_text[max_len_label_text] = '\0';
3343 if (strlen(label_text) > 0)
3344 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3347 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3348 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3349 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3350 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3351 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3352 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3353 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3354 max_len_label_text);
3355 label_text[max_len_label_text] = '\0';
3357 if (strlen(label_text) > 0)
3358 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3360 redraw_mask |= REDRAW_FIELD;
3363 static void DrawPreviewLevelLabel(int mode)
3365 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3368 static void DrawPreviewLevelInfo(int mode)
3370 if (mode == MICROLABEL_LEVEL_NAME)
3371 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3372 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3373 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3376 static void DrawPreviewLevelExt(boolean restart)
3378 static unsigned int scroll_delay = 0;
3379 static unsigned int label_delay = 0;
3380 static int from_x, from_y, scroll_direction;
3381 static int label_state, label_counter;
3382 unsigned int scroll_delay_value = preview.step_delay;
3383 boolean show_level_border = (BorderElement != EL_EMPTY);
3384 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3385 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3392 if (preview.anim_mode == ANIM_CENTERED)
3394 if (level_xsize > preview.xsize)
3395 from_x = (level_xsize - preview.xsize) / 2;
3396 if (level_ysize > preview.ysize)
3397 from_y = (level_ysize - preview.ysize) / 2;
3400 from_x += preview.xoffset;
3401 from_y += preview.yoffset;
3403 scroll_direction = MV_RIGHT;
3407 DrawPreviewLevelPlayfield(from_x, from_y);
3408 DrawPreviewLevelLabel(label_state);
3410 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3411 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3413 // initialize delay counters
3414 DelayReached(&scroll_delay, 0);
3415 DelayReached(&label_delay, 0);
3417 if (leveldir_current->name)
3419 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3420 char label_text[MAX_OUTPUT_LINESIZE + 1];
3421 int font_nr = pos->font;
3422 int max_len_label_text = getMaxTextLength(pos, font_nr);
3424 if (pos->size != -1)
3425 max_len_label_text = pos->size;
3427 strncpy(label_text, leveldir_current->name, max_len_label_text);
3428 label_text[max_len_label_text] = '\0';
3430 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3431 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3437 // scroll preview level, if needed
3438 if (preview.anim_mode != ANIM_NONE &&
3439 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3440 DelayReached(&scroll_delay, scroll_delay_value))
3442 switch (scroll_direction)
3447 from_x -= preview.step_offset;
3448 from_x = (from_x < 0 ? 0 : from_x);
3451 scroll_direction = MV_UP;
3455 if (from_x < level_xsize - preview.xsize)
3457 from_x += preview.step_offset;
3458 from_x = (from_x > level_xsize - preview.xsize ?
3459 level_xsize - preview.xsize : from_x);
3462 scroll_direction = MV_DOWN;
3468 from_y -= preview.step_offset;
3469 from_y = (from_y < 0 ? 0 : from_y);
3472 scroll_direction = MV_RIGHT;
3476 if (from_y < level_ysize - preview.ysize)
3478 from_y += preview.step_offset;
3479 from_y = (from_y > level_ysize - preview.ysize ?
3480 level_ysize - preview.ysize : from_y);
3483 scroll_direction = MV_LEFT;
3490 DrawPreviewLevelPlayfield(from_x, from_y);
3493 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3494 // redraw micro level label, if needed
3495 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3496 !strEqual(level.author, ANONYMOUS_NAME) &&
3497 !strEqual(level.author, leveldir_current->name) &&
3498 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3500 int max_label_counter = 23;
3502 if (leveldir_current->imported_from != NULL &&
3503 strlen(leveldir_current->imported_from) > 0)
3504 max_label_counter += 14;
3505 if (leveldir_current->imported_by != NULL &&
3506 strlen(leveldir_current->imported_by) > 0)
3507 max_label_counter += 14;
3509 label_counter = (label_counter + 1) % max_label_counter;
3510 label_state = (label_counter >= 0 && label_counter <= 7 ?
3511 MICROLABEL_LEVEL_NAME :
3512 label_counter >= 9 && label_counter <= 12 ?
3513 MICROLABEL_LEVEL_AUTHOR_HEAD :
3514 label_counter >= 14 && label_counter <= 21 ?
3515 MICROLABEL_LEVEL_AUTHOR :
3516 label_counter >= 23 && label_counter <= 26 ?
3517 MICROLABEL_IMPORTED_FROM_HEAD :
3518 label_counter >= 28 && label_counter <= 35 ?
3519 MICROLABEL_IMPORTED_FROM :
3520 label_counter >= 37 && label_counter <= 40 ?
3521 MICROLABEL_IMPORTED_BY_HEAD :
3522 label_counter >= 42 && label_counter <= 49 ?
3523 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3525 if (leveldir_current->imported_from == NULL &&
3526 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3527 label_state == MICROLABEL_IMPORTED_FROM))
3528 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3529 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3531 DrawPreviewLevelLabel(label_state);
3535 void DrawPreviewPlayers(void)
3537 if (game_status != GAME_MODE_MAIN)
3540 // do not draw preview players if level preview redefined, but players aren't
3541 if (preview.redefined && !menu.main.preview_players.redefined)
3544 boolean player_found[MAX_PLAYERS];
3545 int num_players = 0;
3548 for (i = 0; i < MAX_PLAYERS; i++)
3549 player_found[i] = FALSE;
3551 // check which players can be found in the level (simple approach)
3552 for (x = 0; x < lev_fieldx; x++)
3554 for (y = 0; y < lev_fieldy; y++)
3556 int element = level.field[x][y];
3558 if (ELEM_IS_PLAYER(element))
3560 int player_nr = GET_PLAYER_NR(element);
3562 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3564 if (!player_found[player_nr])
3567 player_found[player_nr] = TRUE;
3572 struct TextPosInfo *pos = &menu.main.preview_players;
3573 int tile_size = pos->tile_size;
3574 int border_size = pos->border_size;
3575 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3576 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3577 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3578 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3579 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3580 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3581 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3582 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3583 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3584 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3585 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3586 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3588 // clear area in which the players will be drawn
3589 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3590 max_players_width, max_players_height);
3592 if (!network.enabled && !setup.team_mode)
3595 // only draw players if level is suited for team mode
3596 if (num_players < 2)
3599 // draw all players that were found in the level
3600 for (i = 0; i < MAX_PLAYERS; i++)
3602 if (player_found[i])
3604 int graphic = el2img(EL_PLAYER_1 + i);
3606 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3608 xpos += player_xoffset;
3609 ypos += player_yoffset;
3614 void DrawPreviewLevelInitial(void)
3616 DrawPreviewLevelExt(TRUE);
3617 DrawPreviewPlayers();
3620 void DrawPreviewLevelAnimation(void)
3622 DrawPreviewLevelExt(FALSE);
3625 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3626 int border_size, int font_nr)
3628 int graphic = el2img(EL_PLAYER_1 + player_nr);
3629 int font_height = getFontHeight(font_nr);
3630 int player_height = MAX(tile_size, font_height);
3631 int xoffset_text = tile_size + border_size;
3632 int yoffset_text = (player_height - font_height) / 2;
3633 int yoffset_graphic = (player_height - tile_size) / 2;
3634 char *player_name = getNetworkPlayerName(player_nr + 1);
3636 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3638 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3641 static void DrawNetworkPlayersExt(boolean force)
3643 if (game_status != GAME_MODE_MAIN)
3646 if (!network.connected && !force)
3649 // do not draw network players if level preview redefined, but players aren't
3650 if (preview.redefined && !menu.main.network_players.redefined)
3653 int num_players = 0;
3656 for (i = 0; i < MAX_PLAYERS; i++)
3657 if (stored_player[i].connected_network)
3660 struct TextPosInfo *pos = &menu.main.network_players;
3661 int tile_size = pos->tile_size;
3662 int border_size = pos->border_size;
3663 int xoffset_text = tile_size + border_size;
3664 int font_nr = pos->font;
3665 int font_width = getFontWidth(font_nr);
3666 int font_height = getFontHeight(font_nr);
3667 int player_height = MAX(tile_size, font_height);
3668 int player_yoffset = player_height + border_size;
3669 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3670 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3671 int all_players_height = num_players * player_yoffset - border_size;
3672 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3673 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3674 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3676 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3677 max_players_width, max_players_height);
3679 // first draw local network player ...
3680 for (i = 0; i < MAX_PLAYERS; i++)
3682 if (stored_player[i].connected_network &&
3683 stored_player[i].connected_locally)
3685 char *player_name = getNetworkPlayerName(i + 1);
3686 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3687 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3689 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3691 ypos += player_yoffset;
3695 // ... then draw all other network players
3696 for (i = 0; i < MAX_PLAYERS; i++)
3698 if (stored_player[i].connected_network &&
3699 !stored_player[i].connected_locally)
3701 char *player_name = getNetworkPlayerName(i + 1);
3702 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3703 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3705 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3707 ypos += player_yoffset;
3712 void DrawNetworkPlayers(void)
3714 DrawNetworkPlayersExt(FALSE);
3717 void ClearNetworkPlayers(void)
3719 DrawNetworkPlayersExt(TRUE);
3722 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3723 int graphic, int sync_frame,
3726 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3728 if (mask_mode == USE_MASKING)
3729 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3731 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3734 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3735 int graphic, int sync_frame, int mask_mode)
3737 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3739 if (mask_mode == USE_MASKING)
3740 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3742 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3745 static void DrawGraphicAnimation(int x, int y, int graphic)
3747 int lx = LEVELX(x), ly = LEVELY(y);
3749 if (!IN_SCR_FIELD(x, y))
3752 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3753 graphic, GfxFrame[lx][ly], NO_MASKING);
3755 MarkTileDirty(x, y);
3758 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3760 int lx = LEVELX(x), ly = LEVELY(y);
3762 if (!IN_SCR_FIELD(x, y))
3765 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3766 graphic, GfxFrame[lx][ly], NO_MASKING);
3767 MarkTileDirty(x, y);
3770 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3772 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3775 void DrawLevelElementAnimation(int x, int y, int element)
3777 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3779 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3782 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3784 int sx = SCREENX(x), sy = SCREENY(y);
3786 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3789 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3792 DrawGraphicAnimation(sx, sy, graphic);
3795 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3796 DrawLevelFieldCrumbled(x, y);
3798 if (GFX_CRUMBLED(Feld[x][y]))
3799 DrawLevelFieldCrumbled(x, y);
3803 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3805 int sx = SCREENX(x), sy = SCREENY(y);
3808 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3811 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3813 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3816 DrawGraphicAnimation(sx, sy, graphic);
3818 if (GFX_CRUMBLED(element))
3819 DrawLevelFieldCrumbled(x, y);
3822 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3824 if (player->use_murphy)
3826 // this works only because currently only one player can be "murphy" ...
3827 static int last_horizontal_dir = MV_LEFT;
3828 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3830 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3831 last_horizontal_dir = move_dir;
3833 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3835 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3837 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3843 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3846 static boolean equalGraphics(int graphic1, int graphic2)
3848 struct GraphicInfo *g1 = &graphic_info[graphic1];
3849 struct GraphicInfo *g2 = &graphic_info[graphic2];
3851 return (g1->bitmap == g2->bitmap &&
3852 g1->src_x == g2->src_x &&
3853 g1->src_y == g2->src_y &&
3854 g1->anim_frames == g2->anim_frames &&
3855 g1->anim_delay == g2->anim_delay &&
3856 g1->anim_mode == g2->anim_mode);
3859 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3863 DRAW_PLAYER_STAGE_INIT = 0,
3864 DRAW_PLAYER_STAGE_LAST_FIELD,
3865 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3866 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3867 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3868 DRAW_PLAYER_STAGE_PLAYER,
3870 DRAW_PLAYER_STAGE_PLAYER,
3871 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3873 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3874 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3876 NUM_DRAW_PLAYER_STAGES
3879 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3881 static int static_last_player_graphic[MAX_PLAYERS];
3882 static int static_last_player_frame[MAX_PLAYERS];
3883 static boolean static_player_is_opaque[MAX_PLAYERS];
3884 static boolean draw_player[MAX_PLAYERS];
3885 int pnr = player->index_nr;
3887 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3889 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3890 static_last_player_frame[pnr] = player->Frame;
3891 static_player_is_opaque[pnr] = FALSE;
3893 draw_player[pnr] = TRUE;
3896 if (!draw_player[pnr])
3900 if (!IN_LEV_FIELD(player->jx, player->jy))
3902 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3903 printf("DrawPlayerField(): This should never happen!\n");
3905 draw_player[pnr] = FALSE;
3911 int last_player_graphic = static_last_player_graphic[pnr];
3912 int last_player_frame = static_last_player_frame[pnr];
3913 boolean player_is_opaque = static_player_is_opaque[pnr];
3915 int jx = player->jx;
3916 int jy = player->jy;
3917 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3918 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3919 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3920 int last_jx = (player->is_moving ? jx - dx : jx);
3921 int last_jy = (player->is_moving ? jy - dy : jy);
3922 int next_jx = jx + dx;
3923 int next_jy = jy + dy;
3924 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3925 int sx = SCREENX(jx);
3926 int sy = SCREENY(jy);
3927 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3928 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3929 int element = Feld[jx][jy];
3930 int last_element = Feld[last_jx][last_jy];
3931 int action = (player->is_pushing ? ACTION_PUSHING :
3932 player->is_digging ? ACTION_DIGGING :
3933 player->is_collecting ? ACTION_COLLECTING :
3934 player->is_moving ? ACTION_MOVING :
3935 player->is_snapping ? ACTION_SNAPPING :
3936 player->is_dropping ? ACTION_DROPPING :
3937 player->is_waiting ? player->action_waiting :
3940 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3942 // ------------------------------------------------------------------------
3943 // initialize drawing the player
3944 // ------------------------------------------------------------------------
3946 draw_player[pnr] = FALSE;
3948 // GfxElement[][] is set to the element the player is digging or collecting;
3949 // remove also for off-screen player if the player is not moving anymore
3950 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3951 GfxElement[jx][jy] = EL_UNDEFINED;
3953 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3956 if (element == EL_EXPLOSION)
3959 InitPlayerGfxAnimation(player, action, move_dir);
3961 draw_player[pnr] = TRUE;
3963 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3965 // ------------------------------------------------------------------------
3966 // draw things in the field the player is leaving, if needed
3967 // ------------------------------------------------------------------------
3969 if (!IN_SCR_FIELD(sx, sy))
3970 draw_player[pnr] = FALSE;
3972 if (!player->is_moving)
3975 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3977 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3979 if (last_element == EL_DYNAMITE_ACTIVE ||
3980 last_element == EL_EM_DYNAMITE_ACTIVE ||
3981 last_element == EL_SP_DISK_RED_ACTIVE)
3982 DrawDynamite(last_jx, last_jy);
3984 DrawLevelFieldThruMask(last_jx, last_jy);
3986 else if (last_element == EL_DYNAMITE_ACTIVE ||
3987 last_element == EL_EM_DYNAMITE_ACTIVE ||
3988 last_element == EL_SP_DISK_RED_ACTIVE)
3989 DrawDynamite(last_jx, last_jy);
3991 DrawLevelField(last_jx, last_jy);
3993 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3994 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3996 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3998 // ------------------------------------------------------------------------
3999 // draw things behind the player, if needed
4000 // ------------------------------------------------------------------------
4004 DrawLevelElement(jx, jy, Back[jx][jy]);
4009 if (IS_ACTIVE_BOMB(element))
4011 DrawLevelElement(jx, jy, EL_EMPTY);
4016 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4018 int old_element = GfxElement[jx][jy];
4019 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4020 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4022 if (GFX_CRUMBLED(old_element))
4023 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4025 DrawGraphic(sx, sy, old_graphic, frame);
4027 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4028 static_player_is_opaque[pnr] = TRUE;
4032 GfxElement[jx][jy] = EL_UNDEFINED;
4034 // make sure that pushed elements are drawn with correct frame rate
4035 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4037 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4038 GfxFrame[jx][jy] = player->StepFrame;
4040 DrawLevelField(jx, jy);
4043 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4045 // ------------------------------------------------------------------------
4046 // draw things the player is pushing, if needed
4047 // ------------------------------------------------------------------------
4049 if (!player->is_pushing || !player->is_moving)
4052 int gfx_frame = GfxFrame[jx][jy];
4054 if (!IS_MOVING(jx, jy)) // push movement already finished
4056 element = Feld[next_jx][next_jy];
4057 gfx_frame = GfxFrame[next_jx][next_jy];
4060 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4061 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4062 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4064 // draw background element under pushed element (like the Sokoban field)
4065 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4067 // this allows transparent pushing animation over non-black background
4070 DrawLevelElement(jx, jy, Back[jx][jy]);
4072 DrawLevelElement(jx, jy, EL_EMPTY);
4074 if (Back[next_jx][next_jy])
4075 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4077 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4079 else if (Back[next_jx][next_jy])
4080 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4082 int px = SCREENX(jx), py = SCREENY(jy);
4083 int pxx = (TILEX - ABS(sxx)) * dx;
4084 int pyy = (TILEY - ABS(syy)) * dy;
4087 // do not draw (EM style) pushing animation when pushing is finished
4088 // (two-tile animations usually do not contain start and end frame)
4089 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4090 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4092 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4094 // masked drawing is needed for EMC style (double) movement graphics
4095 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4096 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4099 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4101 // ------------------------------------------------------------------------
4102 // draw player himself
4103 // ------------------------------------------------------------------------
4105 int graphic = getPlayerGraphic(player, move_dir);
4107 // in the case of changed player action or direction, prevent the current
4108 // animation frame from being restarted for identical animations
4109 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4110 player->Frame = last_player_frame;
4112 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4114 if (player_is_opaque)
4115 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4117 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4119 if (SHIELD_ON(player))
4121 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4122 IMG_SHIELD_NORMAL_ACTIVE);
4123 frame = getGraphicAnimationFrame(graphic, -1);
4125 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4128 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4130 // ------------------------------------------------------------------------
4131 // draw things in front of player (active dynamite or dynabombs)
4132 // ------------------------------------------------------------------------
4134 if (IS_ACTIVE_BOMB(element))
4136 int graphic = el2img(element);
4137 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4139 if (game.emulation == EMU_SUPAPLEX)
4140 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4142 DrawGraphicThruMask(sx, sy, graphic, frame);
4145 if (player_is_moving && last_element == EL_EXPLOSION)
4147 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4148 GfxElement[last_jx][last_jy] : EL_EMPTY);
4149 int graphic = el_act2img(element, ACTION_EXPLODING);
4150 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4151 int phase = ExplodePhase[last_jx][last_jy] - 1;
4152 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4155 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4158 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4160 // ------------------------------------------------------------------------
4161 // draw elements the player is just walking/passing through/under
4162 // ------------------------------------------------------------------------
4164 if (player_is_moving)
4166 // handle the field the player is leaving ...
4167 if (IS_ACCESSIBLE_INSIDE(last_element))
4168 DrawLevelField(last_jx, last_jy);
4169 else if (IS_ACCESSIBLE_UNDER(last_element))
4170 DrawLevelFieldThruMask(last_jx, last_jy);
4173 // do not redraw accessible elements if the player is just pushing them
4174 if (!player_is_moving || !player->is_pushing)
4176 // ... and the field the player is entering
4177 if (IS_ACCESSIBLE_INSIDE(element))
4178 DrawLevelField(jx, jy);
4179 else if (IS_ACCESSIBLE_UNDER(element))
4180 DrawLevelFieldThruMask(jx, jy);
4183 MarkTileDirty(sx, sy);
4187 void DrawPlayer(struct PlayerInfo *player)
4191 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4192 DrawPlayerExt(player, i);
4195 void DrawAllPlayers(void)
4199 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4200 for (j = 0; j < MAX_PLAYERS; j++)
4201 if (stored_player[j].active)
4202 DrawPlayerExt(&stored_player[j], i);
4205 void DrawPlayerField(int x, int y)
4207 if (!IS_PLAYER(x, y))
4210 DrawPlayer(PLAYERINFO(x, y));
4213 // ----------------------------------------------------------------------------
4215 void WaitForEventToContinue(void)
4217 boolean still_wait = TRUE;
4219 if (program.headless)
4222 // simulate releasing mouse button over last gadget, if still pressed
4224 HandleGadgets(-1, -1, 0);
4226 button_status = MB_RELEASED;
4234 if (NextValidEvent(&event))
4238 case EVENT_BUTTONRELEASE:
4239 case EVENT_KEYPRESS:
4240 case SDL_CONTROLLERBUTTONDOWN:
4241 case SDL_JOYBUTTONDOWN:
4245 case EVENT_KEYRELEASE:
4246 ClearPlayerAction();
4250 HandleOtherEvents(&event);
4254 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4263 #define MAX_REQUEST_LINES 13
4264 #define MAX_REQUEST_LINE_FONT1_LEN 7
4265 #define MAX_REQUEST_LINE_FONT2_LEN 10
4267 static int RequestHandleEvents(unsigned int req_state)
4269 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4271 int width = request.width;
4272 int height = request.height;
4276 // when showing request dialog after game ended, deactivate game panel
4277 if (game_just_ended)
4278 game.panel.active = FALSE;
4280 game.request_active = TRUE;
4282 setRequestPosition(&sx, &sy, FALSE);
4284 button_status = MB_RELEASED;
4286 request_gadget_id = -1;
4291 if (game_just_ended)
4293 // the MM game engine does not use a special (scrollable) field buffer
4294 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4295 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4297 HandleGameActions();
4299 SetDrawtoField(DRAW_TO_BACKBUFFER);
4301 if (global.use_envelope_request)
4303 // copy current state of request area to middle of playfield area
4304 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4312 while (NextValidEvent(&event))
4316 case EVENT_BUTTONPRESS:
4317 case EVENT_BUTTONRELEASE:
4318 case EVENT_MOTIONNOTIFY:
4322 if (event.type == EVENT_MOTIONNOTIFY)
4327 motion_status = TRUE;
4328 mx = ((MotionEvent *) &event)->x;
4329 my = ((MotionEvent *) &event)->y;
4333 motion_status = FALSE;
4334 mx = ((ButtonEvent *) &event)->x;
4335 my = ((ButtonEvent *) &event)->y;
4336 if (event.type == EVENT_BUTTONPRESS)
4337 button_status = ((ButtonEvent *) &event)->button;
4339 button_status = MB_RELEASED;
4342 // this sets 'request_gadget_id'
4343 HandleGadgets(mx, my, button_status);
4345 switch (request_gadget_id)
4347 case TOOL_CTRL_ID_YES:
4348 case TOOL_CTRL_ID_TOUCH_YES:
4351 case TOOL_CTRL_ID_NO:
4352 case TOOL_CTRL_ID_TOUCH_NO:
4355 case TOOL_CTRL_ID_CONFIRM:
4356 case TOOL_CTRL_ID_TOUCH_CONFIRM:
4357 result = TRUE | FALSE;
4360 case TOOL_CTRL_ID_PLAYER_1:
4363 case TOOL_CTRL_ID_PLAYER_2:
4366 case TOOL_CTRL_ID_PLAYER_3:
4369 case TOOL_CTRL_ID_PLAYER_4:
4374 // only check clickable animations if no request gadget clicked
4375 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4382 case SDL_WINDOWEVENT:
4383 HandleWindowEvent((WindowEvent *) &event);
4386 case SDL_APP_WILLENTERBACKGROUND:
4387 case SDL_APP_DIDENTERBACKGROUND:
4388 case SDL_APP_WILLENTERFOREGROUND:
4389 case SDL_APP_DIDENTERFOREGROUND:
4390 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4393 case EVENT_KEYPRESS:
4395 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4400 if (req_state & REQ_CONFIRM)
4409 #if defined(KSYM_Rewind)
4410 case KSYM_Rewind: // for Amazon Fire TV remote
4419 #if defined(KSYM_FastForward)
4420 case KSYM_FastForward: // for Amazon Fire TV remote
4426 HandleKeysDebug(key, KEY_PRESSED);
4430 if (req_state & REQ_PLAYER)
4432 int old_player_nr = setup.network_player_nr;
4435 result = old_player_nr + 1;
4440 result = old_player_nr + 1;
4471 case EVENT_KEYRELEASE:
4472 ClearPlayerAction();
4475 case SDL_CONTROLLERBUTTONDOWN:
4476 switch (event.cbutton.button)
4478 case SDL_CONTROLLER_BUTTON_A:
4479 case SDL_CONTROLLER_BUTTON_X:
4480 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4481 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4485 case SDL_CONTROLLER_BUTTON_B:
4486 case SDL_CONTROLLER_BUTTON_Y:
4487 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4488 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4489 case SDL_CONTROLLER_BUTTON_BACK:
4494 if (req_state & REQ_PLAYER)
4496 int old_player_nr = setup.network_player_nr;
4499 result = old_player_nr + 1;
4501 switch (event.cbutton.button)
4503 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4504 case SDL_CONTROLLER_BUTTON_Y:
4508 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4509 case SDL_CONTROLLER_BUTTON_B:
4513 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4514 case SDL_CONTROLLER_BUTTON_A:
4518 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4519 case SDL_CONTROLLER_BUTTON_X:
4530 case SDL_CONTROLLERBUTTONUP:
4531 HandleJoystickEvent(&event);
4532 ClearPlayerAction();
4536 HandleOtherEvents(&event);
4541 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4543 int joy = AnyJoystick();
4545 if (joy & JOY_BUTTON_1)
4547 else if (joy & JOY_BUTTON_2)
4550 else if (AnyJoystick())
4552 int joy = AnyJoystick();
4554 if (req_state & REQ_PLAYER)
4558 else if (joy & JOY_RIGHT)
4560 else if (joy & JOY_DOWN)
4562 else if (joy & JOY_LEFT)
4567 if (game_just_ended)
4569 if (global.use_envelope_request)
4571 // copy back current state of pressed buttons inside request area
4572 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4579 game.request_active = FALSE;
4584 static boolean RequestDoor(char *text, unsigned int req_state)
4586 unsigned int old_door_state;
4587 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4588 int font_nr = FONT_TEXT_2;
4593 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4595 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4596 font_nr = FONT_TEXT_1;
4599 if (game_status == GAME_MODE_PLAYING)
4600 BlitScreenToBitmap(backbuffer);
4602 // disable deactivated drawing when quick-loading level tape recording
4603 if (tape.playing && tape.deactivate_display)
4604 TapeDeactivateDisplayOff(TRUE);
4606 SetMouseCursor(CURSOR_DEFAULT);
4608 // pause network game while waiting for request to answer
4609 if (network.enabled &&
4610 game_status == GAME_MODE_PLAYING &&
4611 !game.all_players_gone &&
4612 req_state & REQUEST_WAIT_FOR_INPUT)
4613 SendToServer_PausePlaying();
4615 old_door_state = GetDoorState();
4617 // simulate releasing mouse button over last gadget, if still pressed
4619 HandleGadgets(-1, -1, 0);
4623 // draw released gadget before proceeding
4626 if (old_door_state & DOOR_OPEN_1)
4628 CloseDoor(DOOR_CLOSE_1);
4630 // save old door content
4631 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4632 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4635 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4636 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4638 // clear door drawing field
4639 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4641 // force DOOR font inside door area
4642 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4644 // write text for request
4645 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4647 char text_line[max_request_line_len + 1];
4653 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4655 tc = *(text_ptr + tx);
4656 // if (!tc || tc == ' ')
4657 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4661 if ((tc == '?' || tc == '!') && tl == 0)
4671 strncpy(text_line, text_ptr, tl);
4674 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4675 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4676 text_line, font_nr);
4678 text_ptr += tl + (tc == ' ' ? 1 : 0);
4679 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4684 if (req_state & REQ_ASK)
4686 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4687 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4688 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
4689 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
4691 else if (req_state & REQ_CONFIRM)
4693 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4694 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
4696 else if (req_state & REQ_PLAYER)
4698 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4699 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4700 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4701 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4704 // copy request gadgets to door backbuffer
4705 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4707 OpenDoor(DOOR_OPEN_1);
4709 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4711 if (game_status == GAME_MODE_PLAYING)
4713 SetPanelBackground();
4714 SetDrawBackgroundMask(REDRAW_DOOR_1);
4718 SetDrawBackgroundMask(REDRAW_FIELD);
4724 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4726 // ---------- handle request buttons ----------
4727 result = RequestHandleEvents(req_state);
4731 if (!(req_state & REQ_STAY_OPEN))
4733 CloseDoor(DOOR_CLOSE_1);
4735 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4736 (req_state & REQ_REOPEN))
4737 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4742 if (game_status == GAME_MODE_PLAYING)
4744 SetPanelBackground();
4745 SetDrawBackgroundMask(REDRAW_DOOR_1);
4749 SetDrawBackgroundMask(REDRAW_FIELD);
4752 // continue network game after request
4753 if (network.enabled &&
4754 game_status == GAME_MODE_PLAYING &&
4755 !game.all_players_gone &&
4756 req_state & REQUEST_WAIT_FOR_INPUT)
4757 SendToServer_ContinuePlaying();
4759 // restore deactivated drawing when quick-loading level tape recording
4760 if (tape.playing && tape.deactivate_display)
4761 TapeDeactivateDisplayOn();
4766 static boolean RequestEnvelope(char *text, unsigned int req_state)
4770 if (game_status == GAME_MODE_PLAYING)
4771 BlitScreenToBitmap(backbuffer);
4773 // disable deactivated drawing when quick-loading level tape recording
4774 if (tape.playing && tape.deactivate_display)
4775 TapeDeactivateDisplayOff(TRUE);
4777 SetMouseCursor(CURSOR_DEFAULT);
4779 // pause network game while waiting for request to answer
4780 if (network.enabled &&
4781 game_status == GAME_MODE_PLAYING &&
4782 !game.all_players_gone &&
4783 req_state & REQUEST_WAIT_FOR_INPUT)
4784 SendToServer_PausePlaying();
4786 // simulate releasing mouse button over last gadget, if still pressed
4788 HandleGadgets(-1, -1, 0);
4792 // (replace with setting corresponding request background)
4793 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4794 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4796 // clear door drawing field
4797 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4799 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4801 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4803 if (game_status == GAME_MODE_PLAYING)
4805 SetPanelBackground();
4806 SetDrawBackgroundMask(REDRAW_DOOR_1);
4810 SetDrawBackgroundMask(REDRAW_FIELD);
4816 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4818 // ---------- handle request buttons ----------
4819 result = RequestHandleEvents(req_state);
4823 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4827 if (game_status == GAME_MODE_PLAYING)
4829 SetPanelBackground();
4830 SetDrawBackgroundMask(REDRAW_DOOR_1);
4834 SetDrawBackgroundMask(REDRAW_FIELD);
4837 // continue network game after request
4838 if (network.enabled &&
4839 game_status == GAME_MODE_PLAYING &&
4840 !game.all_players_gone &&
4841 req_state & REQUEST_WAIT_FOR_INPUT)
4842 SendToServer_ContinuePlaying();
4844 // restore deactivated drawing when quick-loading level tape recording
4845 if (tape.playing && tape.deactivate_display)
4846 TapeDeactivateDisplayOn();
4851 boolean Request(char *text, unsigned int req_state)
4853 boolean overlay_enabled = GetOverlayEnabled();
4856 SetOverlayEnabled(FALSE);
4858 if (global.use_envelope_request)
4859 result = RequestEnvelope(text, req_state);
4861 result = RequestDoor(text, req_state);
4863 SetOverlayEnabled(overlay_enabled);
4868 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4870 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4871 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4874 if (dpo1->sort_priority != dpo2->sort_priority)
4875 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4877 compare_result = dpo1->nr - dpo2->nr;
4879 return compare_result;
4882 void InitGraphicCompatibilityInfo_Doors(void)
4888 struct DoorInfo *door;
4892 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4893 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4895 { -1, -1, -1, NULL }
4897 struct Rect door_rect_list[] =
4899 { DX, DY, DXSIZE, DYSIZE },
4900 { VX, VY, VXSIZE, VYSIZE }
4904 for (i = 0; doors[i].door_token != -1; i++)
4906 int door_token = doors[i].door_token;
4907 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4908 int part_1 = doors[i].part_1;
4909 int part_8 = doors[i].part_8;
4910 int part_2 = part_1 + 1;
4911 int part_3 = part_1 + 2;
4912 struct DoorInfo *door = doors[i].door;
4913 struct Rect *door_rect = &door_rect_list[door_index];
4914 boolean door_gfx_redefined = FALSE;
4916 // check if any door part graphic definitions have been redefined
4918 for (j = 0; door_part_controls[j].door_token != -1; j++)
4920 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4921 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4923 if (dpc->door_token == door_token && fi->redefined)
4924 door_gfx_redefined = TRUE;
4927 // check for old-style door graphic/animation modifications
4929 if (!door_gfx_redefined)
4931 if (door->anim_mode & ANIM_STATIC_PANEL)
4933 door->panel.step_xoffset = 0;
4934 door->panel.step_yoffset = 0;
4937 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4939 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4940 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4941 int num_door_steps, num_panel_steps;
4943 // remove door part graphics other than the two default wings
4945 for (j = 0; door_part_controls[j].door_token != -1; j++)
4947 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4948 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4950 if (dpc->graphic >= part_3 &&
4951 dpc->graphic <= part_8)
4955 // set graphics and screen positions of the default wings
4957 g_part_1->width = door_rect->width;
4958 g_part_1->height = door_rect->height;
4959 g_part_2->width = door_rect->width;
4960 g_part_2->height = door_rect->height;
4961 g_part_2->src_x = door_rect->width;
4962 g_part_2->src_y = g_part_1->src_y;
4964 door->part_2.x = door->part_1.x;
4965 door->part_2.y = door->part_1.y;
4967 if (door->width != -1)
4969 g_part_1->width = door->width;
4970 g_part_2->width = door->width;
4972 // special treatment for graphics and screen position of right wing
4973 g_part_2->src_x += door_rect->width - door->width;
4974 door->part_2.x += door_rect->width - door->width;
4977 if (door->height != -1)
4979 g_part_1->height = door->height;
4980 g_part_2->height = door->height;
4982 // special treatment for graphics and screen position of bottom wing
4983 g_part_2->src_y += door_rect->height - door->height;
4984 door->part_2.y += door_rect->height - door->height;
4987 // set animation delays for the default wings and panels
4989 door->part_1.step_delay = door->step_delay;
4990 door->part_2.step_delay = door->step_delay;
4991 door->panel.step_delay = door->step_delay;
4993 // set animation draw order for the default wings
4995 door->part_1.sort_priority = 2; // draw left wing over ...
4996 door->part_2.sort_priority = 1; // ... right wing
4998 // set animation draw offset for the default wings
5000 if (door->anim_mode & ANIM_HORIZONTAL)
5002 door->part_1.step_xoffset = door->step_offset;
5003 door->part_1.step_yoffset = 0;
5004 door->part_2.step_xoffset = door->step_offset * -1;
5005 door->part_2.step_yoffset = 0;
5007 num_door_steps = g_part_1->width / door->step_offset;
5009 else // ANIM_VERTICAL
5011 door->part_1.step_xoffset = 0;
5012 door->part_1.step_yoffset = door->step_offset;
5013 door->part_2.step_xoffset = 0;
5014 door->part_2.step_yoffset = door->step_offset * -1;
5016 num_door_steps = g_part_1->height / door->step_offset;
5019 // set animation draw offset for the default panels
5021 if (door->step_offset > 1)
5023 num_panel_steps = 2 * door_rect->height / door->step_offset;
5024 door->panel.start_step = num_panel_steps - num_door_steps;
5025 door->panel.start_step_closing = door->panel.start_step;
5029 num_panel_steps = door_rect->height / door->step_offset;
5030 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5031 door->panel.start_step_closing = door->panel.start_step;
5032 door->panel.step_delay *= 2;
5039 void InitDoors(void)
5043 for (i = 0; door_part_controls[i].door_token != -1; i++)
5045 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5046 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5048 // initialize "start_step_opening" and "start_step_closing", if needed
5049 if (dpc->pos->start_step_opening == 0 &&
5050 dpc->pos->start_step_closing == 0)
5052 // dpc->pos->start_step_opening = dpc->pos->start_step;
5053 dpc->pos->start_step_closing = dpc->pos->start_step;
5056 // fill structure for door part draw order (sorted below)
5058 dpo->sort_priority = dpc->pos->sort_priority;
5061 // sort door part controls according to sort_priority and graphic number
5062 qsort(door_part_order, MAX_DOOR_PARTS,
5063 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5066 unsigned int OpenDoor(unsigned int door_state)
5068 if (door_state & DOOR_COPY_BACK)
5070 if (door_state & DOOR_OPEN_1)
5071 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5072 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5074 if (door_state & DOOR_OPEN_2)
5075 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5076 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5078 door_state &= ~DOOR_COPY_BACK;
5081 return MoveDoor(door_state);
5084 unsigned int CloseDoor(unsigned int door_state)
5086 unsigned int old_door_state = GetDoorState();
5088 if (!(door_state & DOOR_NO_COPY_BACK))
5090 if (old_door_state & DOOR_OPEN_1)
5091 BlitBitmap(backbuffer, bitmap_db_door_1,
5092 DX, DY, DXSIZE, DYSIZE, 0, 0);
5094 if (old_door_state & DOOR_OPEN_2)
5095 BlitBitmap(backbuffer, bitmap_db_door_2,
5096 VX, VY, VXSIZE, VYSIZE, 0, 0);
5098 door_state &= ~DOOR_NO_COPY_BACK;
5101 return MoveDoor(door_state);
5104 unsigned int GetDoorState(void)
5106 return MoveDoor(DOOR_GET_STATE);
5109 unsigned int SetDoorState(unsigned int door_state)
5111 return MoveDoor(door_state | DOOR_SET_STATE);
5114 static int euclid(int a, int b)
5116 return (b ? euclid(b, a % b) : a);
5119 unsigned int MoveDoor(unsigned int door_state)
5121 struct Rect door_rect_list[] =
5123 { DX, DY, DXSIZE, DYSIZE },
5124 { VX, VY, VXSIZE, VYSIZE }
5126 static int door1 = DOOR_CLOSE_1;
5127 static int door2 = DOOR_CLOSE_2;
5128 unsigned int door_delay = 0;
5129 unsigned int door_delay_value;
5132 if (door_state == DOOR_GET_STATE)
5133 return (door1 | door2);
5135 if (door_state & DOOR_SET_STATE)
5137 if (door_state & DOOR_ACTION_1)
5138 door1 = door_state & DOOR_ACTION_1;
5139 if (door_state & DOOR_ACTION_2)
5140 door2 = door_state & DOOR_ACTION_2;
5142 return (door1 | door2);
5145 if (!(door_state & DOOR_FORCE_REDRAW))
5147 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5148 door_state &= ~DOOR_OPEN_1;
5149 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5150 door_state &= ~DOOR_CLOSE_1;
5151 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5152 door_state &= ~DOOR_OPEN_2;
5153 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5154 door_state &= ~DOOR_CLOSE_2;
5157 if (global.autoplay_leveldir)
5159 door_state |= DOOR_NO_DELAY;
5160 door_state &= ~DOOR_CLOSE_ALL;
5163 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5164 door_state |= DOOR_NO_DELAY;
5166 if (door_state & DOOR_ACTION)
5168 boolean door_panel_drawn[NUM_DOORS];
5169 boolean panel_has_doors[NUM_DOORS];
5170 boolean door_part_skip[MAX_DOOR_PARTS];
5171 boolean door_part_done[MAX_DOOR_PARTS];
5172 boolean door_part_done_all;
5173 int num_steps[MAX_DOOR_PARTS];
5174 int max_move_delay = 0; // delay for complete animations of all doors
5175 int max_step_delay = 0; // delay (ms) between two animation frames
5176 int num_move_steps = 0; // number of animation steps for all doors
5177 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5178 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5179 int current_move_delay = 0;
5183 for (i = 0; i < NUM_DOORS; i++)
5184 panel_has_doors[i] = FALSE;
5186 for (i = 0; i < MAX_DOOR_PARTS; i++)
5188 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5189 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5190 int door_token = dpc->door_token;
5192 door_part_done[i] = FALSE;
5193 door_part_skip[i] = (!(door_state & door_token) ||
5197 for (i = 0; i < MAX_DOOR_PARTS; i++)
5199 int nr = door_part_order[i].nr;
5200 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5201 struct DoorPartPosInfo *pos = dpc->pos;
5202 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5203 int door_token = dpc->door_token;
5204 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5205 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5206 int step_xoffset = ABS(pos->step_xoffset);
5207 int step_yoffset = ABS(pos->step_yoffset);
5208 int step_delay = pos->step_delay;
5209 int current_door_state = door_state & door_token;
5210 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5211 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5212 boolean part_opening = (is_panel ? door_closing : door_opening);
5213 int start_step = (part_opening ? pos->start_step_opening :
5214 pos->start_step_closing);
5215 float move_xsize = (step_xoffset ? g->width : 0);
5216 float move_ysize = (step_yoffset ? g->height : 0);
5217 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5218 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5219 int move_steps = (move_xsteps && move_ysteps ?
5220 MIN(move_xsteps, move_ysteps) :
5221 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5222 int move_delay = move_steps * step_delay;
5224 if (door_part_skip[nr])
5227 max_move_delay = MAX(max_move_delay, move_delay);
5228 max_step_delay = (max_step_delay == 0 ? step_delay :
5229 euclid(max_step_delay, step_delay));
5230 num_steps[nr] = move_steps;
5234 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5236 panel_has_doors[door_index] = TRUE;
5240 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5242 num_move_steps = max_move_delay / max_step_delay;
5243 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5245 door_delay_value = max_step_delay;
5247 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5249 start = num_move_steps - 1;
5253 // opening door sound has priority over simultaneously closing door
5254 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5256 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5258 if (door_state & DOOR_OPEN_1)
5259 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5260 if (door_state & DOOR_OPEN_2)
5261 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5263 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5265 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5267 if (door_state & DOOR_CLOSE_1)
5268 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5269 if (door_state & DOOR_CLOSE_2)
5270 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5274 for (k = start; k < num_move_steps; k++)
5276 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5278 door_part_done_all = TRUE;
5280 for (i = 0; i < NUM_DOORS; i++)
5281 door_panel_drawn[i] = FALSE;
5283 for (i = 0; i < MAX_DOOR_PARTS; i++)
5285 int nr = door_part_order[i].nr;
5286 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5287 struct DoorPartPosInfo *pos = dpc->pos;
5288 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5289 int door_token = dpc->door_token;
5290 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5291 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5292 boolean is_panel_and_door_has_closed = FALSE;
5293 struct Rect *door_rect = &door_rect_list[door_index];
5294 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5296 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5297 int current_door_state = door_state & door_token;
5298 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5299 boolean door_closing = !door_opening;
5300 boolean part_opening = (is_panel ? door_closing : door_opening);
5301 boolean part_closing = !part_opening;
5302 int start_step = (part_opening ? pos->start_step_opening :
5303 pos->start_step_closing);
5304 int step_delay = pos->step_delay;
5305 int step_factor = step_delay / max_step_delay;
5306 int k1 = (step_factor ? k / step_factor + 1 : k);
5307 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5308 int kk = MAX(0, k2);
5311 int src_x, src_y, src_xx, src_yy;
5312 int dst_x, dst_y, dst_xx, dst_yy;
5315 if (door_part_skip[nr])
5318 if (!(door_state & door_token))
5326 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5327 int kk_door = MAX(0, k2_door);
5328 int sync_frame = kk_door * door_delay_value;
5329 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5331 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5332 &g_src_x, &g_src_y);
5337 if (!door_panel_drawn[door_index])
5339 ClearRectangle(drawto, door_rect->x, door_rect->y,
5340 door_rect->width, door_rect->height);
5342 door_panel_drawn[door_index] = TRUE;
5345 // draw opening or closing door parts
5347 if (pos->step_xoffset < 0) // door part on right side
5350 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5353 if (dst_xx + width > door_rect->width)
5354 width = door_rect->width - dst_xx;
5356 else // door part on left side
5359 dst_xx = pos->x - kk * pos->step_xoffset;
5363 src_xx = ABS(dst_xx);
5367 width = g->width - src_xx;
5369 if (width > door_rect->width)
5370 width = door_rect->width;
5372 // printf("::: k == %d [%d] \n", k, start_step);
5375 if (pos->step_yoffset < 0) // door part on bottom side
5378 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5381 if (dst_yy + height > door_rect->height)
5382 height = door_rect->height - dst_yy;
5384 else // door part on top side
5387 dst_yy = pos->y - kk * pos->step_yoffset;
5391 src_yy = ABS(dst_yy);
5395 height = g->height - src_yy;
5398 src_x = g_src_x + src_xx;
5399 src_y = g_src_y + src_yy;
5401 dst_x = door_rect->x + dst_xx;
5402 dst_y = door_rect->y + dst_yy;
5404 is_panel_and_door_has_closed =
5407 panel_has_doors[door_index] &&
5408 k >= num_move_steps_doors_only - 1);
5410 if (width >= 0 && width <= g->width &&
5411 height >= 0 && height <= g->height &&
5412 !is_panel_and_door_has_closed)
5414 if (is_panel || !pos->draw_masked)
5415 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5418 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5422 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5424 if ((part_opening && (width < 0 || height < 0)) ||
5425 (part_closing && (width >= g->width && height >= g->height)))
5426 door_part_done[nr] = TRUE;
5428 // continue door part animations, but not panel after door has closed
5429 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5430 door_part_done_all = FALSE;
5433 if (!(door_state & DOOR_NO_DELAY))
5437 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5439 current_move_delay += max_step_delay;
5441 // prevent OS (Windows) from complaining about program not responding
5445 if (door_part_done_all)
5449 if (!(door_state & DOOR_NO_DELAY))
5451 // wait for specified door action post delay
5452 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5453 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5454 else if (door_state & DOOR_ACTION_1)
5455 door_delay_value = door_1.post_delay;
5456 else if (door_state & DOOR_ACTION_2)
5457 door_delay_value = door_2.post_delay;
5459 while (!DelayReached(&door_delay, door_delay_value))
5464 if (door_state & DOOR_ACTION_1)
5465 door1 = door_state & DOOR_ACTION_1;
5466 if (door_state & DOOR_ACTION_2)
5467 door2 = door_state & DOOR_ACTION_2;
5469 // draw masked border over door area
5470 DrawMaskedBorder(REDRAW_DOOR_1);
5471 DrawMaskedBorder(REDRAW_DOOR_2);
5473 ClearAutoRepeatKeyEvents();
5475 return (door1 | door2);
5478 static boolean useSpecialEditorDoor(void)
5480 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5481 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5483 // do not draw special editor door if editor border defined or redefined
5484 if (graphic_info[graphic].bitmap != NULL || redefined)
5487 // do not draw special editor door if global border defined to be empty
5488 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5491 // do not draw special editor door if viewport definitions do not match
5495 EY + EYSIZE != VY + VYSIZE)
5501 void DrawSpecialEditorDoor(void)
5503 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5504 int top_border_width = gfx1->width;
5505 int top_border_height = gfx1->height;
5506 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5507 int ex = EX - outer_border;
5508 int ey = EY - outer_border;
5509 int vy = VY - outer_border;
5510 int exsize = EXSIZE + 2 * outer_border;
5512 if (!useSpecialEditorDoor())
5515 // draw bigger level editor toolbox window
5516 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5517 top_border_width, top_border_height, ex, ey - top_border_height);
5518 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5519 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5521 redraw_mask |= REDRAW_ALL;
5524 void UndrawSpecialEditorDoor(void)
5526 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5527 int top_border_width = gfx1->width;
5528 int top_border_height = gfx1->height;
5529 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5530 int ex = EX - outer_border;
5531 int ey = EY - outer_border;
5532 int ey_top = ey - top_border_height;
5533 int exsize = EXSIZE + 2 * outer_border;
5534 int eysize = EYSIZE + 2 * outer_border;
5536 if (!useSpecialEditorDoor())
5539 // draw normal tape recorder window
5540 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5542 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5543 ex, ey_top, top_border_width, top_border_height,
5545 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5546 ex, ey, exsize, eysize, ex, ey);
5550 // if screen background is set to "[NONE]", clear editor toolbox window
5551 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5552 ClearRectangle(drawto, ex, ey, exsize, eysize);
5555 redraw_mask |= REDRAW_ALL;
5559 // ---------- new tool button stuff -------------------------------------------
5564 struct TextPosInfo *pos;
5566 boolean is_touch_button;
5568 } toolbutton_info[NUM_TOOL_BUTTONS] =
5571 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5572 TOOL_CTRL_ID_YES, FALSE, "yes"
5575 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5576 TOOL_CTRL_ID_NO, FALSE, "no"
5579 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5580 TOOL_CTRL_ID_CONFIRM, FALSE, "confirm"
5583 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5584 TOOL_CTRL_ID_PLAYER_1, FALSE, "player 1"
5587 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5588 TOOL_CTRL_ID_PLAYER_2, FALSE, "player 2"
5591 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5592 TOOL_CTRL_ID_PLAYER_3, FALSE, "player 3"
5595 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5596 TOOL_CTRL_ID_PLAYER_4, FALSE, "player 4"
5599 IMG_GFX_REQUEST_BUTTON_TOUCH_YES, &request.button.touch_yes,
5600 TOOL_CTRL_ID_TOUCH_YES, TRUE, "yes"
5603 IMG_GFX_REQUEST_BUTTON_TOUCH_NO, &request.button.touch_no,
5604 TOOL_CTRL_ID_TOUCH_NO, TRUE, "no"
5607 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM, &request.button.touch_confirm,
5608 TOOL_CTRL_ID_TOUCH_CONFIRM, TRUE, "confirm"
5612 void CreateToolButtons(void)
5616 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5618 int graphic = toolbutton_info[i].graphic;
5619 struct GraphicInfo *gfx = &graphic_info[graphic];
5620 struct TextPosInfo *pos = toolbutton_info[i].pos;
5621 struct GadgetInfo *gi;
5622 Bitmap *deco_bitmap = None;
5623 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5624 unsigned int event_mask = GD_EVENT_RELEASED;
5625 boolean is_touch_button = toolbutton_info[i].is_touch_button;
5626 int base_x = (is_touch_button ? 0 : DX);
5627 int base_y = (is_touch_button ? 0 : DY);
5628 int gd_x = gfx->src_x;
5629 int gd_y = gfx->src_y;
5630 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5631 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5636 if (global.use_envelope_request && !is_touch_button)
5638 setRequestPosition(&base_x, &base_y, TRUE);
5640 // check if request buttons are outside of envelope and fix, if needed
5641 if (x < 0 || x + gfx->width > request.width ||
5642 y < 0 || y + gfx->height > request.height)
5644 if (id == TOOL_CTRL_ID_YES)
5647 y = request.height - 2 * request.border_size - gfx->height;
5649 else if (id == TOOL_CTRL_ID_NO)
5651 x = request.width - 2 * request.border_size - gfx->width;
5652 y = request.height - 2 * request.border_size - gfx->height;
5654 else if (id == TOOL_CTRL_ID_CONFIRM)
5656 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5657 y = request.height - 2 * request.border_size - gfx->height;
5659 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5661 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5663 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5664 y = request.height - 2 * request.border_size - gfx->height * 2;
5666 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5667 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5672 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5674 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5676 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5677 pos->size, &deco_bitmap, &deco_x, &deco_y);
5678 deco_xpos = (gfx->width - pos->size) / 2;
5679 deco_ypos = (gfx->height - pos->size) / 2;
5682 gi = CreateGadget(GDI_CUSTOM_ID, id,
5683 GDI_IMAGE_ID, graphic,
5684 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5687 GDI_WIDTH, gfx->width,
5688 GDI_HEIGHT, gfx->height,
5689 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5690 GDI_STATE, GD_BUTTON_UNPRESSED,
5691 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5692 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5693 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5694 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5695 GDI_DECORATION_SIZE, pos->size, pos->size,
5696 GDI_DECORATION_SHIFTING, 1, 1,
5697 GDI_DIRECT_DRAW, FALSE,
5698 GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
5699 GDI_EVENT_MASK, event_mask,
5700 GDI_CALLBACK_ACTION, HandleToolButtons,
5704 Error(ERR_EXIT, "cannot create gadget");
5706 tool_gadget[id] = gi;
5710 void FreeToolButtons(void)
5714 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5715 FreeGadget(tool_gadget[i]);
5718 static void UnmapToolButtons(void)
5722 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5723 UnmapGadget(tool_gadget[i]);
5726 static void HandleToolButtons(struct GadgetInfo *gi)
5728 request_gadget_id = gi->custom_id;
5731 static struct Mapping_EM_to_RND_object
5734 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5735 boolean is_backside; // backside of moving element
5741 em_object_mapping_list[] =
5744 Xblank, TRUE, FALSE,
5749 Xacid_splash_e, FALSE, FALSE,
5750 EL_ACID_SPLASH_RIGHT, -1, -1
5753 Xacid_splash_w, FALSE, FALSE,
5754 EL_ACID_SPLASH_LEFT, -1, -1
5758 Xplant, TRUE, FALSE,
5759 EL_EMC_PLANT, -1, -1
5762 Yplant, FALSE, FALSE,
5763 EL_EMC_PLANT, -1, -1
5767 Xacid_1, TRUE, FALSE,
5771 Xacid_2, FALSE, FALSE,
5775 Xacid_3, FALSE, FALSE,
5779 Xacid_4, FALSE, FALSE,
5783 Xacid_5, FALSE, FALSE,
5787 Xacid_6, FALSE, FALSE,
5791 Xacid_7, FALSE, FALSE,
5795 Xacid_8, FALSE, FALSE,
5800 Xfake_acid_1, TRUE, FALSE,
5801 EL_EMC_FAKE_ACID, -1, -1
5804 Xfake_acid_2, FALSE, FALSE,
5805 EL_EMC_FAKE_ACID, -1, -1
5808 Xfake_acid_3, FALSE, FALSE,
5809 EL_EMC_FAKE_ACID, -1, -1
5812 Xfake_acid_4, FALSE, FALSE,
5813 EL_EMC_FAKE_ACID, -1, -1
5816 Xfake_acid_5, FALSE, FALSE,
5817 EL_EMC_FAKE_ACID, -1, -1
5820 Xfake_acid_6, FALSE, FALSE,
5821 EL_EMC_FAKE_ACID, -1, -1
5824 Xfake_acid_7, FALSE, FALSE,
5825 EL_EMC_FAKE_ACID, -1, -1
5828 Xfake_acid_8, FALSE, FALSE,
5829 EL_EMC_FAKE_ACID, -1, -1
5833 Xgrass, TRUE, FALSE,
5834 EL_EMC_GRASS, -1, -1
5837 Ygrass_nB, FALSE, FALSE,
5838 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5841 Ygrass_eB, FALSE, FALSE,
5842 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5845 Ygrass_sB, FALSE, FALSE,
5846 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5849 Ygrass_wB, FALSE, FALSE,
5850 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5858 Ydirt_nB, FALSE, FALSE,
5859 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5862 Ydirt_eB, FALSE, FALSE,
5863 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5866 Ydirt_sB, FALSE, FALSE,
5867 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5870 Ydirt_wB, FALSE, FALSE,
5871 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5875 Xandroid, TRUE, FALSE,
5876 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5879 Xandroid_1_n, FALSE, FALSE,
5880 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5883 Xandroid_2_n, FALSE, FALSE,
5884 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5887 Xandroid_1_e, FALSE, FALSE,
5888 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5891 Xandroid_2_e, FALSE, FALSE,
5892 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5895 Xandroid_1_w, FALSE, FALSE,
5896 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5899 Xandroid_2_w, FALSE, FALSE,
5900 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5903 Xandroid_1_s, FALSE, FALSE,
5904 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5907 Xandroid_2_s, FALSE, FALSE,
5908 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5911 Yandroid_n, FALSE, FALSE,
5912 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5915 Yandroid_nB, FALSE, TRUE,
5916 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5919 Yandroid_ne, FALSE, FALSE,
5920 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5923 Yandroid_neB, FALSE, TRUE,
5924 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5927 Yandroid_e, FALSE, FALSE,
5928 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5931 Yandroid_eB, FALSE, TRUE,
5932 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5935 Yandroid_se, FALSE, FALSE,
5936 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5939 Yandroid_seB, FALSE, TRUE,
5940 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5943 Yandroid_s, FALSE, FALSE,
5944 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5947 Yandroid_sB, FALSE, TRUE,
5948 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5951 Yandroid_sw, FALSE, FALSE,
5952 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5955 Yandroid_swB, FALSE, TRUE,
5956 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5959 Yandroid_w, FALSE, FALSE,
5960 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5963 Yandroid_wB, FALSE, TRUE,
5964 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5967 Yandroid_nw, FALSE, FALSE,
5968 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5971 Yandroid_nwB, FALSE, TRUE,
5972 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5976 Xeater_n, TRUE, FALSE,
5977 EL_YAMYAM_UP, -1, -1
5980 Xeater_e, TRUE, FALSE,
5981 EL_YAMYAM_RIGHT, -1, -1
5984 Xeater_w, TRUE, FALSE,
5985 EL_YAMYAM_LEFT, -1, -1
5988 Xeater_s, TRUE, FALSE,
5989 EL_YAMYAM_DOWN, -1, -1
5992 Yeater_n, FALSE, FALSE,
5993 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5996 Yeater_nB, FALSE, TRUE,
5997 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6000 Yeater_e, FALSE, FALSE,
6001 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6004 Yeater_eB, FALSE, TRUE,
6005 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6008 Yeater_s, FALSE, FALSE,
6009 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6012 Yeater_sB, FALSE, TRUE,
6013 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6016 Yeater_w, FALSE, FALSE,
6017 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6020 Yeater_wB, FALSE, TRUE,
6021 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6024 Yeater_stone, FALSE, FALSE,
6025 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6028 Yeater_spring, FALSE, FALSE,
6029 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6033 Xalien, TRUE, FALSE,
6037 Xalien_pause, FALSE, FALSE,
6041 Yalien_n, FALSE, FALSE,
6042 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6045 Yalien_nB, FALSE, TRUE,
6046 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6049 Yalien_e, FALSE, FALSE,
6050 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6053 Yalien_eB, FALSE, TRUE,
6054 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6057 Yalien_s, FALSE, FALSE,
6058 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6061 Yalien_sB, FALSE, TRUE,
6062 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6065 Yalien_w, FALSE, FALSE,
6066 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6069 Yalien_wB, FALSE, TRUE,
6070 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6073 Yalien_stone, FALSE, FALSE,
6074 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6077 Yalien_spring, FALSE, FALSE,
6078 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6082 Xbug_1_n, TRUE, FALSE,
6086 Xbug_1_e, TRUE, FALSE,
6087 EL_BUG_RIGHT, -1, -1
6090 Xbug_1_s, TRUE, FALSE,
6094 Xbug_1_w, TRUE, FALSE,
6098 Xbug_2_n, FALSE, FALSE,
6102 Xbug_2_e, FALSE, FALSE,
6103 EL_BUG_RIGHT, -1, -1
6106 Xbug_2_s, FALSE, FALSE,
6110 Xbug_2_w, FALSE, FALSE,
6114 Ybug_n, FALSE, FALSE,
6115 EL_BUG, ACTION_MOVING, MV_BIT_UP
6118 Ybug_nB, FALSE, TRUE,
6119 EL_BUG, ACTION_MOVING, MV_BIT_UP
6122 Ybug_e, FALSE, FALSE,
6123 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6126 Ybug_eB, FALSE, TRUE,
6127 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
6130 Ybug_s, FALSE, FALSE,
6131 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6134 Ybug_sB, FALSE, TRUE,
6135 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
6138 Ybug_w, FALSE, FALSE,
6139 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6142 Ybug_wB, FALSE, TRUE,
6143 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
6146 Ybug_w_n, FALSE, FALSE,
6147 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6150 Ybug_n_e, FALSE, FALSE,
6151 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6154 Ybug_e_s, FALSE, FALSE,
6155 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6158 Ybug_s_w, FALSE, FALSE,
6159 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6162 Ybug_e_n, FALSE, FALSE,
6163 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6166 Ybug_s_e, FALSE, FALSE,
6167 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6170 Ybug_w_s, FALSE, FALSE,
6171 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6174 Ybug_n_w, FALSE, FALSE,
6175 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6178 Ybug_stone, FALSE, FALSE,
6179 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
6182 Ybug_spring, FALSE, FALSE,
6183 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
6187 Xtank_1_n, TRUE, FALSE,
6188 EL_SPACESHIP_UP, -1, -1
6191 Xtank_1_e, TRUE, FALSE,
6192 EL_SPACESHIP_RIGHT, -1, -1
6195 Xtank_1_s, TRUE, FALSE,
6196 EL_SPACESHIP_DOWN, -1, -1
6199 Xtank_1_w, TRUE, FALSE,
6200 EL_SPACESHIP_LEFT, -1, -1
6203 Xtank_2_n, FALSE, FALSE,
6204 EL_SPACESHIP_UP, -1, -1
6207 Xtank_2_e, FALSE, FALSE,
6208 EL_SPACESHIP_RIGHT, -1, -1
6211 Xtank_2_s, FALSE, FALSE,
6212 EL_SPACESHIP_DOWN, -1, -1
6215 Xtank_2_w, FALSE, FALSE,
6216 EL_SPACESHIP_LEFT, -1, -1
6219 Ytank_n, FALSE, FALSE,
6220 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6223 Ytank_nB, FALSE, TRUE,
6224 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6227 Ytank_e, FALSE, FALSE,
6228 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6231 Ytank_eB, FALSE, TRUE,
6232 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6235 Ytank_s, FALSE, FALSE,
6236 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6239 Ytank_sB, FALSE, TRUE,
6240 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6243 Ytank_w, FALSE, FALSE,
6244 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6247 Ytank_wB, FALSE, TRUE,
6248 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6251 Ytank_w_n, FALSE, FALSE,
6252 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6255 Ytank_n_e, FALSE, FALSE,
6256 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6259 Ytank_e_s, FALSE, FALSE,
6260 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6263 Ytank_s_w, FALSE, FALSE,
6264 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6267 Ytank_e_n, FALSE, FALSE,
6268 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6271 Ytank_s_e, FALSE, FALSE,
6272 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6275 Ytank_w_s, FALSE, FALSE,
6276 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6279 Ytank_n_w, FALSE, FALSE,
6280 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6283 Ytank_stone, FALSE, FALSE,
6284 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6287 Ytank_spring, FALSE, FALSE,
6288 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6292 Xemerald, TRUE, FALSE,
6296 Xemerald_pause, FALSE, FALSE,
6300 Xemerald_fall, FALSE, FALSE,
6304 Xemerald_shine, FALSE, FALSE,
6305 EL_EMERALD, ACTION_TWINKLING, -1
6308 Yemerald_s, FALSE, FALSE,
6309 EL_EMERALD, ACTION_FALLING, -1
6312 Yemerald_sB, FALSE, TRUE,
6313 EL_EMERALD, ACTION_FALLING, -1
6316 Yemerald_e, FALSE, FALSE,
6317 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6320 Yemerald_eB, FALSE, TRUE,
6321 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6324 Yemerald_w, FALSE, FALSE,
6325 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6328 Yemerald_wB, FALSE, TRUE,
6329 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6332 Yemerald_blank, FALSE, FALSE,
6333 EL_EMERALD, ACTION_COLLECTING, -1
6337 Xdiamond, TRUE, FALSE,
6341 Xdiamond_pause, FALSE, FALSE,
6345 Xdiamond_fall, FALSE, FALSE,
6349 Xdiamond_shine, FALSE, FALSE,
6350 EL_DIAMOND, ACTION_TWINKLING, -1
6353 Ydiamond_s, FALSE, FALSE,
6354 EL_DIAMOND, ACTION_FALLING, -1
6357 Ydiamond_sB, FALSE, TRUE,
6358 EL_DIAMOND, ACTION_FALLING, -1
6361 Ydiamond_e, FALSE, FALSE,
6362 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6365 Ydiamond_eB, FALSE, TRUE,
6366 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6369 Ydiamond_w, FALSE, FALSE,
6370 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6373 Ydiamond_wB, FALSE, TRUE,
6374 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6377 Ydiamond_blank, FALSE, FALSE,
6378 EL_DIAMOND, ACTION_COLLECTING, -1
6381 Ydiamond_stone, FALSE, FALSE,
6382 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6386 Xstone, TRUE, FALSE,
6390 Xstone_pause, FALSE, FALSE,
6394 Xstone_fall, FALSE, FALSE,
6398 Ystone_s, FALSE, FALSE,
6399 EL_ROCK, ACTION_FALLING, -1
6402 Ystone_sB, FALSE, TRUE,
6403 EL_ROCK, ACTION_FALLING, -1
6406 Ystone_e, FALSE, FALSE,
6407 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6410 Ystone_eB, FALSE, TRUE,
6411 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
6414 Ystone_w, FALSE, FALSE,
6415 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6418 Ystone_wB, FALSE, TRUE,
6419 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
6427 Xbomb_pause, FALSE, FALSE,
6431 Xbomb_fall, FALSE, FALSE,
6435 Ybomb_s, FALSE, FALSE,
6436 EL_BOMB, ACTION_FALLING, -1
6439 Ybomb_sB, FALSE, TRUE,
6440 EL_BOMB, ACTION_FALLING, -1
6443 Ybomb_e, FALSE, FALSE,
6444 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6447 Ybomb_eB, FALSE, TRUE,
6448 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6451 Ybomb_w, FALSE, FALSE,
6452 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6455 Ybomb_wB, FALSE, TRUE,
6456 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6459 Ybomb_blank, FALSE, FALSE,
6460 EL_BOMB, ACTION_ACTIVATING, -1
6468 Xnut_pause, FALSE, FALSE,
6472 Xnut_fall, FALSE, FALSE,
6476 Ynut_s, FALSE, FALSE,
6477 EL_NUT, ACTION_FALLING, -1
6480 Ynut_sB, FALSE, TRUE,
6481 EL_NUT, ACTION_FALLING, -1
6484 Ynut_e, FALSE, FALSE,
6485 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6488 Ynut_eB, FALSE, TRUE,
6489 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
6492 Ynut_w, FALSE, FALSE,
6493 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6496 Ynut_wB, FALSE, TRUE,
6497 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
6500 Ynut_stone, FALSE, FALSE,
6501 EL_NUT, ACTION_BREAKING, -1
6505 Xspring, TRUE, FALSE,
6509 Xspring_pause, FALSE, FALSE,
6513 Xspring_e, FALSE, FALSE,
6517 Xspring_w, FALSE, FALSE,
6521 Xspring_fall, FALSE, FALSE,
6525 Yspring_s, FALSE, FALSE,
6526 EL_SPRING, ACTION_FALLING, -1
6529 Yspring_sB, FALSE, TRUE,
6530 EL_SPRING, ACTION_FALLING, -1
6533 Yspring_e, FALSE, FALSE,
6534 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6537 Yspring_eB, FALSE, TRUE,
6538 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6541 Yspring_w, FALSE, FALSE,
6542 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6545 Yspring_wB, FALSE, TRUE,
6546 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6549 Yspring_alien_e, FALSE, FALSE,
6550 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6553 Yspring_alien_eB, FALSE, TRUE,
6554 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6557 Yspring_alien_w, FALSE, FALSE,
6558 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6561 Yspring_alien_wB, FALSE, TRUE,
6562 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6566 Xpush_emerald_e, FALSE, FALSE,
6567 EL_EMERALD, -1, MV_BIT_RIGHT
6570 Xpush_emerald_w, FALSE, FALSE,
6571 EL_EMERALD, -1, MV_BIT_LEFT
6574 Xpush_diamond_e, FALSE, FALSE,
6575 EL_DIAMOND, -1, MV_BIT_RIGHT
6578 Xpush_diamond_w, FALSE, FALSE,
6579 EL_DIAMOND, -1, MV_BIT_LEFT
6582 Xpush_stone_e, FALSE, FALSE,
6583 EL_ROCK, -1, MV_BIT_RIGHT
6586 Xpush_stone_w, FALSE, FALSE,
6587 EL_ROCK, -1, MV_BIT_LEFT
6590 Xpush_bomb_e, FALSE, FALSE,
6591 EL_BOMB, -1, MV_BIT_RIGHT
6594 Xpush_bomb_w, FALSE, FALSE,
6595 EL_BOMB, -1, MV_BIT_LEFT
6598 Xpush_nut_e, FALSE, FALSE,
6599 EL_NUT, -1, MV_BIT_RIGHT
6602 Xpush_nut_w, FALSE, FALSE,
6603 EL_NUT, -1, MV_BIT_LEFT
6606 Xpush_spring_e, FALSE, FALSE,
6607 EL_SPRING, -1, MV_BIT_RIGHT
6610 Xpush_spring_w, FALSE, FALSE,
6611 EL_SPRING, -1, MV_BIT_LEFT
6615 Xdynamite, TRUE, FALSE,
6616 EL_EM_DYNAMITE, -1, -1
6619 Ydynamite_blank, FALSE, FALSE,
6620 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6623 Xdynamite_1, TRUE, FALSE,
6624 EL_EM_DYNAMITE_ACTIVE, -1, -1
6627 Xdynamite_2, FALSE, FALSE,
6628 EL_EM_DYNAMITE_ACTIVE, -1, -1
6631 Xdynamite_3, FALSE, FALSE,
6632 EL_EM_DYNAMITE_ACTIVE, -1, -1
6635 Xdynamite_4, FALSE, FALSE,
6636 EL_EM_DYNAMITE_ACTIVE, -1, -1
6640 Xkey_1, TRUE, FALSE,
6644 Xkey_2, TRUE, FALSE,
6648 Xkey_3, TRUE, FALSE,
6652 Xkey_4, TRUE, FALSE,
6656 Xkey_5, TRUE, FALSE,
6657 EL_EMC_KEY_5, -1, -1
6660 Xkey_6, TRUE, FALSE,
6661 EL_EMC_KEY_6, -1, -1
6664 Xkey_7, TRUE, FALSE,
6665 EL_EMC_KEY_7, -1, -1
6668 Xkey_8, TRUE, FALSE,
6669 EL_EMC_KEY_8, -1, -1
6673 Xdoor_1, TRUE, FALSE,
6674 EL_EM_GATE_1, -1, -1
6677 Xdoor_2, TRUE, FALSE,
6678 EL_EM_GATE_2, -1, -1
6681 Xdoor_3, TRUE, FALSE,
6682 EL_EM_GATE_3, -1, -1
6685 Xdoor_4, TRUE, FALSE,
6686 EL_EM_GATE_4, -1, -1
6689 Xdoor_5, TRUE, FALSE,
6690 EL_EMC_GATE_5, -1, -1
6693 Xdoor_6, TRUE, FALSE,
6694 EL_EMC_GATE_6, -1, -1
6697 Xdoor_7, TRUE, FALSE,
6698 EL_EMC_GATE_7, -1, -1
6701 Xdoor_8, TRUE, FALSE,
6702 EL_EMC_GATE_8, -1, -1
6706 Xfake_door_1, TRUE, FALSE,
6707 EL_EM_GATE_1_GRAY, -1, -1
6710 Xfake_door_2, TRUE, FALSE,
6711 EL_EM_GATE_2_GRAY, -1, -1
6714 Xfake_door_3, TRUE, FALSE,
6715 EL_EM_GATE_3_GRAY, -1, -1
6718 Xfake_door_4, TRUE, FALSE,
6719 EL_EM_GATE_4_GRAY, -1, -1
6722 Xfake_door_5, TRUE, FALSE,
6723 EL_EMC_GATE_5_GRAY, -1, -1
6726 Xfake_door_6, TRUE, FALSE,
6727 EL_EMC_GATE_6_GRAY, -1, -1
6730 Xfake_door_7, TRUE, FALSE,
6731 EL_EMC_GATE_7_GRAY, -1, -1
6734 Xfake_door_8, TRUE, FALSE,
6735 EL_EMC_GATE_8_GRAY, -1, -1
6739 Xballoon, TRUE, FALSE,
6743 Yballoon_n, FALSE, FALSE,
6744 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6747 Yballoon_nB, FALSE, TRUE,
6748 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6751 Yballoon_e, FALSE, FALSE,
6752 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6755 Yballoon_eB, FALSE, TRUE,
6756 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6759 Yballoon_s, FALSE, FALSE,
6760 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6763 Yballoon_sB, FALSE, TRUE,
6764 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6767 Yballoon_w, FALSE, FALSE,
6768 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6771 Yballoon_wB, FALSE, TRUE,
6772 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6776 Xball_1, TRUE, FALSE,
6777 EL_EMC_MAGIC_BALL, -1, -1
6780 Yball_1, FALSE, FALSE,
6781 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6784 Xball_2, FALSE, FALSE,
6785 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6788 Yball_2, FALSE, FALSE,
6789 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6792 Yball_blank, FALSE, FALSE,
6793 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6797 Xamoeba_1, TRUE, FALSE,
6798 EL_AMOEBA_DRY, ACTION_OTHER, -1
6801 Xamoeba_2, FALSE, FALSE,
6802 EL_AMOEBA_DRY, ACTION_OTHER, -1
6805 Xamoeba_3, FALSE, FALSE,
6806 EL_AMOEBA_DRY, ACTION_OTHER, -1
6809 Xamoeba_4, FALSE, FALSE,
6810 EL_AMOEBA_DRY, ACTION_OTHER, -1
6813 Xamoeba_5, TRUE, FALSE,
6814 EL_AMOEBA_WET, ACTION_OTHER, -1
6817 Xamoeba_6, FALSE, FALSE,
6818 EL_AMOEBA_WET, ACTION_OTHER, -1
6821 Xamoeba_7, FALSE, FALSE,
6822 EL_AMOEBA_WET, ACTION_OTHER, -1
6825 Xamoeba_8, FALSE, FALSE,
6826 EL_AMOEBA_WET, ACTION_OTHER, -1
6830 Xdrip, FALSE, FALSE,
6831 EL_AMOEBA_DROP, ACTION_GROWING, -1
6834 Xdrip_fall, TRUE, FALSE,
6835 EL_AMOEBA_DROP, -1, -1
6838 Xdrip_stretch, FALSE, FALSE,
6839 EL_AMOEBA_DROP, ACTION_FALLING, -1
6842 Xdrip_stretchB, FALSE, TRUE,
6843 EL_AMOEBA_DROP, ACTION_FALLING, -1
6846 Ydrip_1_s, FALSE, FALSE,
6847 EL_AMOEBA_DROP, ACTION_FALLING, -1
6850 Ydrip_1_sB, FALSE, TRUE,
6851 EL_AMOEBA_DROP, ACTION_FALLING, -1
6854 Ydrip_2_s, FALSE, FALSE,
6855 EL_AMOEBA_DROP, ACTION_FALLING, -1
6858 Ydrip_2_sB, FALSE, TRUE,
6859 EL_AMOEBA_DROP, ACTION_FALLING, -1
6863 Xwonderwall, TRUE, FALSE,
6864 EL_MAGIC_WALL, -1, -1
6867 XwonderwallB, FALSE, FALSE,
6868 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6872 Xwheel, TRUE, FALSE,
6873 EL_ROBOT_WHEEL, -1, -1
6876 XwheelB, FALSE, FALSE,
6877 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6881 Xswitch, TRUE, FALSE,
6882 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6885 XswitchB, FALSE, FALSE,
6886 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6890 Xbumper, TRUE, FALSE,
6891 EL_EMC_SPRING_BUMPER, -1, -1
6894 XbumperB, FALSE, FALSE,
6895 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6899 Xacid_nw, TRUE, FALSE,
6900 EL_ACID_POOL_TOPLEFT, -1, -1
6903 Xacid_ne, TRUE, FALSE,
6904 EL_ACID_POOL_TOPRIGHT, -1, -1
6907 Xacid_sw, TRUE, FALSE,
6908 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6911 Xacid_s, TRUE, FALSE,
6912 EL_ACID_POOL_BOTTOM, -1, -1
6915 Xacid_se, TRUE, FALSE,
6916 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6920 Xfake_blank, TRUE, FALSE,
6921 EL_INVISIBLE_WALL, -1, -1
6924 Xfake_blankB, FALSE, FALSE,
6925 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6929 Xfake_grass, TRUE, FALSE,
6930 EL_EMC_FAKE_GRASS, -1, -1
6933 Xfake_grassB, FALSE, FALSE,
6934 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6938 Xfake_amoeba, TRUE, FALSE,
6939 EL_EMC_DRIPPER, -1, -1
6942 Xfake_amoebaB, FALSE, FALSE,
6943 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6947 Xlenses, TRUE, FALSE,
6948 EL_EMC_LENSES, -1, -1
6952 Xmagnify, TRUE, FALSE,
6953 EL_EMC_MAGNIFIER, -1, -1
6958 EL_QUICKSAND_EMPTY, -1, -1
6961 Xsand_stone, TRUE, FALSE,
6962 EL_QUICKSAND_FULL, -1, -1
6965 Xsand_stonein_1, FALSE, TRUE,
6966 EL_ROCK, ACTION_FILLING, -1
6969 Xsand_stonein_2, FALSE, TRUE,
6970 EL_ROCK, ACTION_FILLING, -1
6973 Xsand_stonein_3, FALSE, TRUE,
6974 EL_ROCK, ACTION_FILLING, -1
6977 Xsand_stonein_4, FALSE, TRUE,
6978 EL_ROCK, ACTION_FILLING, -1
6981 Xsand_sandstone_1, FALSE, FALSE,
6982 EL_QUICKSAND_FILLING, -1, -1
6985 Xsand_sandstone_2, FALSE, FALSE,
6986 EL_QUICKSAND_FILLING, -1, -1
6989 Xsand_sandstone_3, FALSE, FALSE,
6990 EL_QUICKSAND_FILLING, -1, -1
6993 Xsand_sandstone_4, FALSE, FALSE,
6994 EL_QUICKSAND_FILLING, -1, -1
6997 Xsand_stonesand_1, FALSE, FALSE,
6998 EL_QUICKSAND_EMPTYING, -1, -1
7001 Xsand_stonesand_2, FALSE, FALSE,
7002 EL_QUICKSAND_EMPTYING, -1, -1
7005 Xsand_stonesand_3, FALSE, FALSE,
7006 EL_QUICKSAND_EMPTYING, -1, -1
7009 Xsand_stonesand_4, FALSE, FALSE,
7010 EL_QUICKSAND_EMPTYING, -1, -1
7013 Xsand_stoneout_1, FALSE, FALSE,
7014 EL_ROCK, ACTION_EMPTYING, -1
7017 Xsand_stoneout_2, FALSE, FALSE,
7018 EL_ROCK, ACTION_EMPTYING, -1
7021 Xsand_stonesand_quickout_1, FALSE, FALSE,
7022 EL_QUICKSAND_EMPTYING, -1, -1
7025 Xsand_stonesand_quickout_2, FALSE, FALSE,
7026 EL_QUICKSAND_EMPTYING, -1, -1
7030 Xslidewall_ns, TRUE, FALSE,
7031 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
7034 Yslidewall_ns_blank, FALSE, FALSE,
7035 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
7038 Xslidewall_ew, TRUE, FALSE,
7039 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
7042 Yslidewall_ew_blank, FALSE, FALSE,
7043 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
7047 Xwind_n, TRUE, FALSE,
7048 EL_BALLOON_SWITCH_UP, -1, -1
7051 Xwind_e, TRUE, FALSE,
7052 EL_BALLOON_SWITCH_RIGHT, -1, -1
7055 Xwind_s, TRUE, FALSE,
7056 EL_BALLOON_SWITCH_DOWN, -1, -1
7059 Xwind_w, TRUE, FALSE,
7060 EL_BALLOON_SWITCH_LEFT, -1, -1
7063 Xwind_any, TRUE, FALSE,
7064 EL_BALLOON_SWITCH_ANY, -1, -1
7067 Xwind_stop, TRUE, FALSE,
7068 EL_BALLOON_SWITCH_NONE, -1, -1
7073 EL_EM_EXIT_CLOSED, -1, -1
7076 Xexit_1, TRUE, FALSE,
7077 EL_EM_EXIT_OPEN, -1, -1
7080 Xexit_2, FALSE, FALSE,
7081 EL_EM_EXIT_OPEN, -1, -1
7084 Xexit_3, FALSE, FALSE,
7085 EL_EM_EXIT_OPEN, -1, -1
7089 Xpause, FALSE, FALSE,
7094 Xwall_1, TRUE, FALSE,
7098 Xwall_2, TRUE, FALSE,
7099 EL_EMC_WALL_14, -1, -1
7102 Xwall_3, TRUE, FALSE,
7103 EL_EMC_WALL_15, -1, -1
7106 Xwall_4, TRUE, FALSE,
7107 EL_EMC_WALL_16, -1, -1
7111 Xroundwall_1, TRUE, FALSE,
7112 EL_WALL_SLIPPERY, -1, -1
7115 Xroundwall_2, TRUE, FALSE,
7116 EL_EMC_WALL_SLIPPERY_2, -1, -1
7119 Xroundwall_3, TRUE, FALSE,
7120 EL_EMC_WALL_SLIPPERY_3, -1, -1
7123 Xroundwall_4, TRUE, FALSE,
7124 EL_EMC_WALL_SLIPPERY_4, -1, -1
7128 Xsteel_1, TRUE, FALSE,
7129 EL_STEELWALL, -1, -1
7132 Xsteel_2, TRUE, FALSE,
7133 EL_EMC_STEELWALL_2, -1, -1
7136 Xsteel_3, TRUE, FALSE,
7137 EL_EMC_STEELWALL_3, -1, -1
7140 Xsteel_4, TRUE, FALSE,
7141 EL_EMC_STEELWALL_4, -1, -1
7145 Xdecor_1, TRUE, FALSE,
7146 EL_EMC_WALL_8, -1, -1
7149 Xdecor_2, TRUE, FALSE,
7150 EL_EMC_WALL_6, -1, -1
7153 Xdecor_3, TRUE, FALSE,
7154 EL_EMC_WALL_4, -1, -1
7157 Xdecor_4, TRUE, FALSE,
7158 EL_EMC_WALL_7, -1, -1
7161 Xdecor_5, TRUE, FALSE,
7162 EL_EMC_WALL_5, -1, -1
7165 Xdecor_6, TRUE, FALSE,
7166 EL_EMC_WALL_9, -1, -1
7169 Xdecor_7, TRUE, FALSE,
7170 EL_EMC_WALL_10, -1, -1
7173 Xdecor_8, TRUE, FALSE,
7174 EL_EMC_WALL_1, -1, -1
7177 Xdecor_9, TRUE, FALSE,
7178 EL_EMC_WALL_2, -1, -1
7181 Xdecor_10, TRUE, FALSE,
7182 EL_EMC_WALL_3, -1, -1
7185 Xdecor_11, TRUE, FALSE,
7186 EL_EMC_WALL_11, -1, -1
7189 Xdecor_12, TRUE, FALSE,
7190 EL_EMC_WALL_12, -1, -1
7194 Xalpha_0, TRUE, FALSE,
7195 EL_CHAR('0'), -1, -1
7198 Xalpha_1, TRUE, FALSE,
7199 EL_CHAR('1'), -1, -1
7202 Xalpha_2, TRUE, FALSE,
7203 EL_CHAR('2'), -1, -1
7206 Xalpha_3, TRUE, FALSE,
7207 EL_CHAR('3'), -1, -1
7210 Xalpha_4, TRUE, FALSE,
7211 EL_CHAR('4'), -1, -1
7214 Xalpha_5, TRUE, FALSE,
7215 EL_CHAR('5'), -1, -1
7218 Xalpha_6, TRUE, FALSE,
7219 EL_CHAR('6'), -1, -1
7222 Xalpha_7, TRUE, FALSE,
7223 EL_CHAR('7'), -1, -1
7226 Xalpha_8, TRUE, FALSE,
7227 EL_CHAR('8'), -1, -1
7230 Xalpha_9, TRUE, FALSE,
7231 EL_CHAR('9'), -1, -1
7234 Xalpha_excla, TRUE, FALSE,
7235 EL_CHAR('!'), -1, -1
7238 Xalpha_quote, TRUE, FALSE,
7239 EL_CHAR('"'), -1, -1
7242 Xalpha_comma, TRUE, FALSE,
7243 EL_CHAR(','), -1, -1
7246 Xalpha_minus, TRUE, FALSE,
7247 EL_CHAR('-'), -1, -1
7250 Xalpha_perio, TRUE, FALSE,
7251 EL_CHAR('.'), -1, -1
7254 Xalpha_colon, TRUE, FALSE,
7255 EL_CHAR(':'), -1, -1
7258 Xalpha_quest, TRUE, FALSE,
7259 EL_CHAR('?'), -1, -1
7262 Xalpha_a, TRUE, FALSE,
7263 EL_CHAR('A'), -1, -1
7266 Xalpha_b, TRUE, FALSE,
7267 EL_CHAR('B'), -1, -1
7270 Xalpha_c, TRUE, FALSE,
7271 EL_CHAR('C'), -1, -1
7274 Xalpha_d, TRUE, FALSE,
7275 EL_CHAR('D'), -1, -1
7278 Xalpha_e, TRUE, FALSE,
7279 EL_CHAR('E'), -1, -1
7282 Xalpha_f, TRUE, FALSE,
7283 EL_CHAR('F'), -1, -1
7286 Xalpha_g, TRUE, FALSE,
7287 EL_CHAR('G'), -1, -1
7290 Xalpha_h, TRUE, FALSE,
7291 EL_CHAR('H'), -1, -1
7294 Xalpha_i, TRUE, FALSE,
7295 EL_CHAR('I'), -1, -1
7298 Xalpha_j, TRUE, FALSE,
7299 EL_CHAR('J'), -1, -1
7302 Xalpha_k, TRUE, FALSE,
7303 EL_CHAR('K'), -1, -1
7306 Xalpha_l, TRUE, FALSE,
7307 EL_CHAR('L'), -1, -1
7310 Xalpha_m, TRUE, FALSE,
7311 EL_CHAR('M'), -1, -1
7314 Xalpha_n, TRUE, FALSE,
7315 EL_CHAR('N'), -1, -1
7318 Xalpha_o, TRUE, FALSE,
7319 EL_CHAR('O'), -1, -1
7322 Xalpha_p, TRUE, FALSE,
7323 EL_CHAR('P'), -1, -1
7326 Xalpha_q, TRUE, FALSE,
7327 EL_CHAR('Q'), -1, -1
7330 Xalpha_r, TRUE, FALSE,
7331 EL_CHAR('R'), -1, -1
7334 Xalpha_s, TRUE, FALSE,
7335 EL_CHAR('S'), -1, -1
7338 Xalpha_t, TRUE, FALSE,
7339 EL_CHAR('T'), -1, -1
7342 Xalpha_u, TRUE, FALSE,
7343 EL_CHAR('U'), -1, -1
7346 Xalpha_v, TRUE, FALSE,
7347 EL_CHAR('V'), -1, -1
7350 Xalpha_w, TRUE, FALSE,
7351 EL_CHAR('W'), -1, -1
7354 Xalpha_x, TRUE, FALSE,
7355 EL_CHAR('X'), -1, -1
7358 Xalpha_y, TRUE, FALSE,
7359 EL_CHAR('Y'), -1, -1
7362 Xalpha_z, TRUE, FALSE,
7363 EL_CHAR('Z'), -1, -1
7366 Xalpha_arrow_e, TRUE, FALSE,
7367 EL_CHAR('>'), -1, -1
7370 Xalpha_arrow_w, TRUE, FALSE,
7371 EL_CHAR('<'), -1, -1
7374 Xalpha_copyr, TRUE, FALSE,
7375 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7379 Ykey_1_blank, FALSE, FALSE,
7380 EL_EM_KEY_1, ACTION_COLLECTING, -1
7383 Ykey_2_blank, FALSE, FALSE,
7384 EL_EM_KEY_2, ACTION_COLLECTING, -1
7387 Ykey_3_blank, FALSE, FALSE,
7388 EL_EM_KEY_3, ACTION_COLLECTING, -1
7391 Ykey_4_blank, FALSE, FALSE,
7392 EL_EM_KEY_4, ACTION_COLLECTING, -1
7395 Ykey_5_blank, FALSE, FALSE,
7396 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7399 Ykey_6_blank, FALSE, FALSE,
7400 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7403 Ykey_7_blank, FALSE, FALSE,
7404 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7407 Ykey_8_blank, FALSE, FALSE,
7408 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7411 Ylenses_blank, FALSE, FALSE,
7412 EL_EMC_LENSES, ACTION_COLLECTING, -1
7415 Ymagnify_blank, FALSE, FALSE,
7416 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7419 Ygrass_blank, FALSE, FALSE,
7420 EL_EMC_GRASS, ACTION_SNAPPING, -1
7423 Ydirt_blank, FALSE, FALSE,
7424 EL_SAND, ACTION_SNAPPING, -1
7428 Xboom_bug, FALSE, FALSE,
7429 EL_BUG, ACTION_EXPLODING, -1
7432 Xboom_bomb, FALSE, FALSE,
7433 EL_BOMB, ACTION_EXPLODING, -1
7436 Xboom_android, FALSE, FALSE,
7437 EL_EMC_ANDROID, ACTION_OTHER, -1
7440 Xboom_1, FALSE, FALSE,
7441 EL_DEFAULT, ACTION_EXPLODING, -1
7444 Xboom_2, FALSE, FALSE,
7445 EL_DEFAULT, ACTION_EXPLODING, -1
7448 Znormal, FALSE, FALSE,
7452 Zdynamite, FALSE, FALSE,
7456 Zplayer, FALSE, FALSE,
7460 Zborder, FALSE, FALSE,
7470 static struct Mapping_EM_to_RND_player
7479 em_player_mapping_list[] =
7483 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7487 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7491 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7495 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7499 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7503 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7507 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7511 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7515 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7519 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7523 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7527 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7531 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7535 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7539 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7543 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7547 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7551 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7555 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7559 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7563 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7567 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7571 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7575 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7579 EL_PLAYER_1, ACTION_DEFAULT, -1,
7583 EL_PLAYER_2, ACTION_DEFAULT, -1,
7587 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7591 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7595 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7599 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7603 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7607 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7611 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7615 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7619 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7623 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7627 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7631 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7635 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7639 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7643 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7647 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7651 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7655 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7659 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7663 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7667 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7671 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7675 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7679 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7683 EL_PLAYER_3, ACTION_DEFAULT, -1,
7687 EL_PLAYER_4, ACTION_DEFAULT, -1,
7696 int map_element_RND_to_EM(int element_rnd)
7698 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7699 static boolean mapping_initialized = FALSE;
7701 if (!mapping_initialized)
7705 // return "Xalpha_quest" for all undefined elements in mapping array
7706 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7707 mapping_RND_to_EM[i] = Xalpha_quest;
7709 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7710 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7711 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7712 em_object_mapping_list[i].element_em;
7714 mapping_initialized = TRUE;
7717 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7718 return mapping_RND_to_EM[element_rnd];
7720 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7725 int map_element_EM_to_RND(int element_em)
7727 static unsigned short mapping_EM_to_RND[TILE_MAX];
7728 static boolean mapping_initialized = FALSE;
7730 if (!mapping_initialized)
7734 // return "EL_UNKNOWN" for all undefined elements in mapping array
7735 for (i = 0; i < TILE_MAX; i++)
7736 mapping_EM_to_RND[i] = EL_UNKNOWN;
7738 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7739 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7740 em_object_mapping_list[i].element_rnd;
7742 mapping_initialized = TRUE;
7745 if (element_em >= 0 && element_em < TILE_MAX)
7746 return mapping_EM_to_RND[element_em];
7748 Error(ERR_WARN, "invalid EM level element %d", element_em);
7753 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7755 struct LevelInfo_EM *level_em = level->native_em_level;
7756 struct LEVEL *lev = level_em->lev;
7759 for (i = 0; i < TILE_MAX; i++)
7760 lev->android_array[i] = Xblank;
7762 for (i = 0; i < level->num_android_clone_elements; i++)
7764 int element_rnd = level->android_clone_element[i];
7765 int element_em = map_element_RND_to_EM(element_rnd);
7767 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7768 if (em_object_mapping_list[j].element_rnd == element_rnd)
7769 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7773 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7775 struct LevelInfo_EM *level_em = level->native_em_level;
7776 struct LEVEL *lev = level_em->lev;
7779 level->num_android_clone_elements = 0;
7781 for (i = 0; i < TILE_MAX; i++)
7783 int element_em = lev->android_array[i];
7785 boolean element_found = FALSE;
7787 if (element_em == Xblank)
7790 element_rnd = map_element_EM_to_RND(element_em);
7792 for (j = 0; j < level->num_android_clone_elements; j++)
7793 if (level->android_clone_element[j] == element_rnd)
7794 element_found = TRUE;
7798 level->android_clone_element[level->num_android_clone_elements++] =
7801 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7806 if (level->num_android_clone_elements == 0)
7808 level->num_android_clone_elements = 1;
7809 level->android_clone_element[0] = EL_EMPTY;
7813 int map_direction_RND_to_EM(int direction)
7815 return (direction == MV_UP ? 0 :
7816 direction == MV_RIGHT ? 1 :
7817 direction == MV_DOWN ? 2 :
7818 direction == MV_LEFT ? 3 :
7822 int map_direction_EM_to_RND(int direction)
7824 return (direction == 0 ? MV_UP :
7825 direction == 1 ? MV_RIGHT :
7826 direction == 2 ? MV_DOWN :
7827 direction == 3 ? MV_LEFT :
7831 int map_element_RND_to_SP(int element_rnd)
7833 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7835 if (element_rnd >= EL_SP_START &&
7836 element_rnd <= EL_SP_END)
7837 element_sp = element_rnd - EL_SP_START;
7838 else if (element_rnd == EL_EMPTY_SPACE)
7840 else if (element_rnd == EL_INVISIBLE_WALL)
7846 int map_element_SP_to_RND(int element_sp)
7848 int element_rnd = EL_UNKNOWN;
7850 if (element_sp >= 0x00 &&
7852 element_rnd = EL_SP_START + element_sp;
7853 else if (element_sp == 0x28)
7854 element_rnd = EL_INVISIBLE_WALL;
7859 int map_action_SP_to_RND(int action_sp)
7863 case actActive: return ACTION_ACTIVE;
7864 case actImpact: return ACTION_IMPACT;
7865 case actExploding: return ACTION_EXPLODING;
7866 case actDigging: return ACTION_DIGGING;
7867 case actSnapping: return ACTION_SNAPPING;
7868 case actCollecting: return ACTION_COLLECTING;
7869 case actPassing: return ACTION_PASSING;
7870 case actPushing: return ACTION_PUSHING;
7871 case actDropping: return ACTION_DROPPING;
7873 default: return ACTION_DEFAULT;
7877 int map_element_RND_to_MM(int element_rnd)
7879 return (element_rnd >= EL_MM_START_1 &&
7880 element_rnd <= EL_MM_END_1 ?
7881 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7883 element_rnd >= EL_MM_START_2 &&
7884 element_rnd <= EL_MM_END_2 ?
7885 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7887 element_rnd >= EL_CHAR_START &&
7888 element_rnd <= EL_CHAR_END ?
7889 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7891 element_rnd >= EL_MM_RUNTIME_START &&
7892 element_rnd <= EL_MM_RUNTIME_END ?
7893 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7895 element_rnd >= EL_MM_DUMMY_START &&
7896 element_rnd <= EL_MM_DUMMY_END ?
7897 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7899 EL_MM_EMPTY_NATIVE);
7902 int map_element_MM_to_RND(int element_mm)
7904 return (element_mm == EL_MM_EMPTY_NATIVE ||
7905 element_mm == EL_DF_EMPTY_NATIVE ?
7908 element_mm >= EL_MM_START_1_NATIVE &&
7909 element_mm <= EL_MM_END_1_NATIVE ?
7910 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7912 element_mm >= EL_MM_START_2_NATIVE &&
7913 element_mm <= EL_MM_END_2_NATIVE ?
7914 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7916 element_mm >= EL_MM_CHAR_START_NATIVE &&
7917 element_mm <= EL_MM_CHAR_END_NATIVE ?
7918 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7920 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7921 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7922 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7924 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7925 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7926 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7931 int map_action_MM_to_RND(int action_mm)
7933 // all MM actions are defined to exactly match their RND counterparts
7937 int map_sound_MM_to_RND(int sound_mm)
7941 case SND_MM_GAME_LEVELTIME_CHARGING:
7942 return SND_GAME_LEVELTIME_CHARGING;
7944 case SND_MM_GAME_HEALTH_CHARGING:
7945 return SND_GAME_HEALTH_CHARGING;
7948 return SND_UNDEFINED;
7952 int map_mm_wall_element(int element)
7954 return (element >= EL_MM_STEEL_WALL_START &&
7955 element <= EL_MM_STEEL_WALL_END ?
7958 element >= EL_MM_WOODEN_WALL_START &&
7959 element <= EL_MM_WOODEN_WALL_END ?
7962 element >= EL_MM_ICE_WALL_START &&
7963 element <= EL_MM_ICE_WALL_END ?
7966 element >= EL_MM_AMOEBA_WALL_START &&
7967 element <= EL_MM_AMOEBA_WALL_END ?
7970 element >= EL_DF_STEEL_WALL_START &&
7971 element <= EL_DF_STEEL_WALL_END ?
7974 element >= EL_DF_WOODEN_WALL_START &&
7975 element <= EL_DF_WOODEN_WALL_END ?
7981 int map_mm_wall_element_editor(int element)
7985 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7986 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7987 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7988 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7989 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7990 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7992 default: return element;
7996 int get_next_element(int element)
8000 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
8001 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
8002 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
8003 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
8004 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8005 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8006 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8007 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8008 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8009 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8010 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8012 default: return element;
8016 int el2img_mm(int element_mm)
8018 return el2img(map_element_MM_to_RND(element_mm));
8021 int el_act_dir2img(int element, int action, int direction)
8023 element = GFX_ELEMENT(element);
8024 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8026 // direction_graphic[][] == graphic[] for undefined direction graphics
8027 return element_info[element].direction_graphic[action][direction];
8030 static int el_act_dir2crm(int element, int action, int direction)
8032 element = GFX_ELEMENT(element);
8033 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8035 // direction_graphic[][] == graphic[] for undefined direction graphics
8036 return element_info[element].direction_crumbled[action][direction];
8039 int el_act2img(int element, int action)
8041 element = GFX_ELEMENT(element);
8043 return element_info[element].graphic[action];
8046 int el_act2crm(int element, int action)
8048 element = GFX_ELEMENT(element);
8050 return element_info[element].crumbled[action];
8053 int el_dir2img(int element, int direction)
8055 element = GFX_ELEMENT(element);
8057 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8060 int el2baseimg(int element)
8062 return element_info[element].graphic[ACTION_DEFAULT];
8065 int el2img(int element)
8067 element = GFX_ELEMENT(element);
8069 return element_info[element].graphic[ACTION_DEFAULT];
8072 int el2edimg(int element)
8074 element = GFX_ELEMENT(element);
8076 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8079 int el2preimg(int element)
8081 element = GFX_ELEMENT(element);
8083 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8086 int el2panelimg(int element)
8088 element = GFX_ELEMENT(element);
8090 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8093 int font2baseimg(int font_nr)
8095 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8098 int getBeltNrFromBeltElement(int element)
8100 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8101 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8102 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8105 int getBeltNrFromBeltActiveElement(int element)
8107 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8108 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8109 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8112 int getBeltNrFromBeltSwitchElement(int element)
8114 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8115 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8116 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8119 int getBeltDirNrFromBeltElement(int element)
8121 static int belt_base_element[4] =
8123 EL_CONVEYOR_BELT_1_LEFT,
8124 EL_CONVEYOR_BELT_2_LEFT,
8125 EL_CONVEYOR_BELT_3_LEFT,
8126 EL_CONVEYOR_BELT_4_LEFT
8129 int belt_nr = getBeltNrFromBeltElement(element);
8130 int belt_dir_nr = element - belt_base_element[belt_nr];
8132 return (belt_dir_nr % 3);
8135 int getBeltDirNrFromBeltSwitchElement(int element)
8137 static int belt_base_element[4] =
8139 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8140 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8141 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8142 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8145 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8146 int belt_dir_nr = element - belt_base_element[belt_nr];
8148 return (belt_dir_nr % 3);
8151 int getBeltDirFromBeltElement(int element)
8153 static int belt_move_dir[3] =
8160 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8162 return belt_move_dir[belt_dir_nr];
8165 int getBeltDirFromBeltSwitchElement(int element)
8167 static int belt_move_dir[3] =
8174 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8176 return belt_move_dir[belt_dir_nr];
8179 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8181 static int belt_base_element[4] =
8183 EL_CONVEYOR_BELT_1_LEFT,
8184 EL_CONVEYOR_BELT_2_LEFT,
8185 EL_CONVEYOR_BELT_3_LEFT,
8186 EL_CONVEYOR_BELT_4_LEFT
8189 return belt_base_element[belt_nr] + belt_dir_nr;
8192 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8194 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8196 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8199 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8201 static int belt_base_element[4] =
8203 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8204 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8205 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8206 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8209 return belt_base_element[belt_nr] + belt_dir_nr;
8212 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8214 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8216 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8219 boolean getTeamMode_EM(void)
8221 return game.team_mode || network_playing;
8224 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8226 int game_frame_delay_value;
8228 game_frame_delay_value =
8229 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8230 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8233 if (tape.playing && tape.warp_forward && !tape.pausing)
8234 game_frame_delay_value = 0;
8236 return game_frame_delay_value;
8239 unsigned int InitRND(int seed)
8241 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8242 return InitEngineRandom_EM(seed);
8243 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8244 return InitEngineRandom_SP(seed);
8245 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8246 return InitEngineRandom_MM(seed);
8248 return InitEngineRandom_RND(seed);
8251 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8252 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8254 static int get_effective_element_EM(int tile, int frame_em)
8256 int element = object_mapping[tile].element_rnd;
8257 int action = object_mapping[tile].action;
8258 boolean is_backside = object_mapping[tile].is_backside;
8259 boolean action_removing = (action == ACTION_DIGGING ||
8260 action == ACTION_SNAPPING ||
8261 action == ACTION_COLLECTING);
8267 case Xacid_splash_e:
8268 case Xacid_splash_w:
8269 return (frame_em > 5 ? EL_EMPTY : element);
8275 else // frame_em == 7
8279 case Xacid_splash_e:
8280 case Xacid_splash_w:
8286 case Ydiamond_stone:
8290 case Xdrip_stretchB:
8306 case Ymagnify_blank:
8309 case Xsand_stonein_1:
8310 case Xsand_stonein_2:
8311 case Xsand_stonein_3:
8312 case Xsand_stonein_4:
8316 return (is_backside || action_removing ? EL_EMPTY : element);
8321 static boolean check_linear_animation_EM(int tile)
8325 case Xsand_stonesand_1:
8326 case Xsand_stonesand_quickout_1:
8327 case Xsand_sandstone_1:
8328 case Xsand_stonein_1:
8329 case Xsand_stoneout_1:
8348 case Xacid_splash_e:
8349 case Xacid_splash_w:
8357 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8358 boolean has_crumbled_graphics,
8359 int crumbled, int sync_frame)
8361 // if element can be crumbled, but certain action graphics are just empty
8362 // space (like instantly snapping sand to empty space in 1 frame), do not
8363 // treat these empty space graphics as crumbled graphics in EMC engine
8364 if (crumbled == IMG_EMPTY_SPACE)
8365 has_crumbled_graphics = FALSE;
8367 if (has_crumbled_graphics)
8369 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8370 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8371 g_crumbled->anim_delay,
8372 g_crumbled->anim_mode,
8373 g_crumbled->anim_start_frame,
8376 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8377 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8379 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8380 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8382 g_em->has_crumbled_graphics = TRUE;
8386 g_em->crumbled_bitmap = NULL;
8387 g_em->crumbled_src_x = 0;
8388 g_em->crumbled_src_y = 0;
8389 g_em->crumbled_border_size = 0;
8390 g_em->crumbled_tile_size = 0;
8392 g_em->has_crumbled_graphics = FALSE;
8397 void ResetGfxAnimation_EM(int x, int y, int tile)
8403 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8404 int tile, int frame_em, int x, int y)
8406 int action = object_mapping[tile].action;
8407 int direction = object_mapping[tile].direction;
8408 int effective_element = get_effective_element_EM(tile, frame_em);
8409 int graphic = (direction == MV_NONE ?
8410 el_act2img(effective_element, action) :
8411 el_act_dir2img(effective_element, action, direction));
8412 struct GraphicInfo *g = &graphic_info[graphic];
8414 boolean action_removing = (action == ACTION_DIGGING ||
8415 action == ACTION_SNAPPING ||
8416 action == ACTION_COLLECTING);
8417 boolean action_moving = (action == ACTION_FALLING ||
8418 action == ACTION_MOVING ||
8419 action == ACTION_PUSHING ||
8420 action == ACTION_EATING ||
8421 action == ACTION_FILLING ||
8422 action == ACTION_EMPTYING);
8423 boolean action_falling = (action == ACTION_FALLING ||
8424 action == ACTION_FILLING ||
8425 action == ACTION_EMPTYING);
8427 // special case: graphic uses "2nd movement tile" and has defined
8428 // 7 frames for movement animation (or less) => use default graphic
8429 // for last (8th) frame which ends the movement animation
8430 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8432 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8433 graphic = (direction == MV_NONE ?
8434 el_act2img(effective_element, action) :
8435 el_act_dir2img(effective_element, action, direction));
8437 g = &graphic_info[graphic];
8440 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8444 else if (action_moving)
8446 boolean is_backside = object_mapping[tile].is_backside;
8450 int direction = object_mapping[tile].direction;
8451 int move_dir = (action_falling ? MV_DOWN : direction);
8456 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8457 if (g->double_movement && frame_em == 0)
8461 if (move_dir == MV_LEFT)
8462 GfxFrame[x - 1][y] = GfxFrame[x][y];
8463 else if (move_dir == MV_RIGHT)
8464 GfxFrame[x + 1][y] = GfxFrame[x][y];
8465 else if (move_dir == MV_UP)
8466 GfxFrame[x][y - 1] = GfxFrame[x][y];
8467 else if (move_dir == MV_DOWN)
8468 GfxFrame[x][y + 1] = GfxFrame[x][y];
8475 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8476 if (tile == Xsand_stonesand_quickout_1 ||
8477 tile == Xsand_stonesand_quickout_2)
8481 if (graphic_info[graphic].anim_global_sync)
8482 sync_frame = FrameCounter;
8483 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8484 sync_frame = GfxFrame[x][y];
8486 sync_frame = 0; // playfield border (pseudo steel)
8488 SetRandomAnimationValue(x, y);
8490 int frame = getAnimationFrame(g->anim_frames,
8493 g->anim_start_frame,
8496 g_em->unique_identifier =
8497 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8500 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8501 int tile, int frame_em, int x, int y)
8503 int action = object_mapping[tile].action;
8504 int direction = object_mapping[tile].direction;
8505 boolean is_backside = object_mapping[tile].is_backside;
8506 int effective_element = get_effective_element_EM(tile, frame_em);
8507 int effective_action = action;
8508 int graphic = (direction == MV_NONE ?
8509 el_act2img(effective_element, effective_action) :
8510 el_act_dir2img(effective_element, effective_action,
8512 int crumbled = (direction == MV_NONE ?
8513 el_act2crm(effective_element, effective_action) :
8514 el_act_dir2crm(effective_element, effective_action,
8516 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8517 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8518 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8519 struct GraphicInfo *g = &graphic_info[graphic];
8522 // special case: graphic uses "2nd movement tile" and has defined
8523 // 7 frames for movement animation (or less) => use default graphic
8524 // for last (8th) frame which ends the movement animation
8525 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8527 effective_action = ACTION_DEFAULT;
8528 graphic = (direction == MV_NONE ?
8529 el_act2img(effective_element, effective_action) :
8530 el_act_dir2img(effective_element, effective_action,
8532 crumbled = (direction == MV_NONE ?
8533 el_act2crm(effective_element, effective_action) :
8534 el_act_dir2crm(effective_element, effective_action,
8537 g = &graphic_info[graphic];
8540 if (graphic_info[graphic].anim_global_sync)
8541 sync_frame = FrameCounter;
8542 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8543 sync_frame = GfxFrame[x][y];
8545 sync_frame = 0; // playfield border (pseudo steel)
8547 SetRandomAnimationValue(x, y);
8549 int frame = getAnimationFrame(g->anim_frames,
8552 g->anim_start_frame,
8555 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8556 g->double_movement && is_backside);
8558 // (updating the "crumbled" graphic definitions is probably not really needed,
8559 // as animations for crumbled graphics can't be longer than one EMC cycle)
8560 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8564 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8565 int player_nr, int anim, int frame_em)
8567 int element = player_mapping[player_nr][anim].element_rnd;
8568 int action = player_mapping[player_nr][anim].action;
8569 int direction = player_mapping[player_nr][anim].direction;
8570 int graphic = (direction == MV_NONE ?
8571 el_act2img(element, action) :
8572 el_act_dir2img(element, action, direction));
8573 struct GraphicInfo *g = &graphic_info[graphic];
8576 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8578 stored_player[player_nr].StepFrame = frame_em;
8580 sync_frame = stored_player[player_nr].Frame;
8582 int frame = getAnimationFrame(g->anim_frames,
8585 g->anim_start_frame,
8588 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8589 &g_em->src_x, &g_em->src_y, FALSE);
8592 void InitGraphicInfo_EM(void)
8597 int num_em_gfx_errors = 0;
8599 if (graphic_info_em_object[0][0].bitmap == NULL)
8601 // EM graphics not yet initialized in em_open_all()
8606 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8609 // always start with reliable default values
8610 for (i = 0; i < TILE_MAX; i++)
8612 object_mapping[i].element_rnd = EL_UNKNOWN;
8613 object_mapping[i].is_backside = FALSE;
8614 object_mapping[i].action = ACTION_DEFAULT;
8615 object_mapping[i].direction = MV_NONE;
8618 // always start with reliable default values
8619 for (p = 0; p < MAX_PLAYERS; p++)
8621 for (i = 0; i < PLY_MAX; i++)
8623 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8624 player_mapping[p][i].action = ACTION_DEFAULT;
8625 player_mapping[p][i].direction = MV_NONE;
8629 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8631 int e = em_object_mapping_list[i].element_em;
8633 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8634 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8636 if (em_object_mapping_list[i].action != -1)
8637 object_mapping[e].action = em_object_mapping_list[i].action;
8639 if (em_object_mapping_list[i].direction != -1)
8640 object_mapping[e].direction =
8641 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8644 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8646 int a = em_player_mapping_list[i].action_em;
8647 int p = em_player_mapping_list[i].player_nr;
8649 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8651 if (em_player_mapping_list[i].action != -1)
8652 player_mapping[p][a].action = em_player_mapping_list[i].action;
8654 if (em_player_mapping_list[i].direction != -1)
8655 player_mapping[p][a].direction =
8656 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8659 for (i = 0; i < TILE_MAX; i++)
8661 int element = object_mapping[i].element_rnd;
8662 int action = object_mapping[i].action;
8663 int direction = object_mapping[i].direction;
8664 boolean is_backside = object_mapping[i].is_backside;
8665 boolean action_exploding = ((action == ACTION_EXPLODING ||
8666 action == ACTION_SMASHED_BY_ROCK ||
8667 action == ACTION_SMASHED_BY_SPRING) &&
8668 element != EL_DIAMOND);
8669 boolean action_active = (action == ACTION_ACTIVE);
8670 boolean action_other = (action == ACTION_OTHER);
8672 for (j = 0; j < 8; j++)
8674 int effective_element = get_effective_element_EM(i, j);
8675 int effective_action = (j < 7 ? action :
8676 i == Xdrip_stretch ? action :
8677 i == Xdrip_stretchB ? action :
8678 i == Ydrip_1_s ? action :
8679 i == Ydrip_1_sB ? action :
8680 i == Yball_1 ? action :
8681 i == Xball_2 ? action :
8682 i == Yball_2 ? action :
8683 i == Yball_blank ? action :
8684 i == Ykey_1_blank ? action :
8685 i == Ykey_2_blank ? action :
8686 i == Ykey_3_blank ? action :
8687 i == Ykey_4_blank ? action :
8688 i == Ykey_5_blank ? action :
8689 i == Ykey_6_blank ? action :
8690 i == Ykey_7_blank ? action :
8691 i == Ykey_8_blank ? action :
8692 i == Ylenses_blank ? action :
8693 i == Ymagnify_blank ? action :
8694 i == Ygrass_blank ? action :
8695 i == Ydirt_blank ? action :
8696 i == Xsand_stonein_1 ? action :
8697 i == Xsand_stonein_2 ? action :
8698 i == Xsand_stonein_3 ? action :
8699 i == Xsand_stonein_4 ? action :
8700 i == Xsand_stoneout_1 ? action :
8701 i == Xsand_stoneout_2 ? action :
8702 i == Xboom_android ? ACTION_EXPLODING :
8703 action_exploding ? ACTION_EXPLODING :
8704 action_active ? action :
8705 action_other ? action :
8707 int graphic = (el_act_dir2img(effective_element, effective_action,
8709 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8711 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8712 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8713 boolean has_action_graphics = (graphic != base_graphic);
8714 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8715 struct GraphicInfo *g = &graphic_info[graphic];
8716 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8719 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8720 boolean special_animation = (action != ACTION_DEFAULT &&
8721 g->anim_frames == 3 &&
8722 g->anim_delay == 2 &&
8723 g->anim_mode & ANIM_LINEAR);
8724 int sync_frame = (i == Xdrip_stretch ? 7 :
8725 i == Xdrip_stretchB ? 7 :
8726 i == Ydrip_2_s ? j + 8 :
8727 i == Ydrip_2_sB ? j + 8 :
8736 i == Xfake_acid_1 ? 0 :
8737 i == Xfake_acid_2 ? 10 :
8738 i == Xfake_acid_3 ? 20 :
8739 i == Xfake_acid_4 ? 30 :
8740 i == Xfake_acid_5 ? 40 :
8741 i == Xfake_acid_6 ? 50 :
8742 i == Xfake_acid_7 ? 60 :
8743 i == Xfake_acid_8 ? 70 :
8745 i == Yball_2 ? j + 8 :
8746 i == Yball_blank ? j + 1 :
8747 i == Ykey_1_blank ? j + 1 :
8748 i == Ykey_2_blank ? j + 1 :
8749 i == Ykey_3_blank ? j + 1 :
8750 i == Ykey_4_blank ? j + 1 :
8751 i == Ykey_5_blank ? j + 1 :
8752 i == Ykey_6_blank ? j + 1 :
8753 i == Ykey_7_blank ? j + 1 :
8754 i == Ykey_8_blank ? j + 1 :
8755 i == Ylenses_blank ? j + 1 :
8756 i == Ymagnify_blank ? j + 1 :
8757 i == Ygrass_blank ? j + 1 :
8758 i == Ydirt_blank ? j + 1 :
8759 i == Xamoeba_1 ? 0 :
8760 i == Xamoeba_2 ? 1 :
8761 i == Xamoeba_3 ? 2 :
8762 i == Xamoeba_4 ? 3 :
8763 i == Xamoeba_5 ? 0 :
8764 i == Xamoeba_6 ? 1 :
8765 i == Xamoeba_7 ? 2 :
8766 i == Xamoeba_8 ? 3 :
8767 i == Xexit_2 ? j + 8 :
8768 i == Xexit_3 ? j + 16 :
8769 i == Xdynamite_1 ? 0 :
8770 i == Xdynamite_2 ? 8 :
8771 i == Xdynamite_3 ? 16 :
8772 i == Xdynamite_4 ? 24 :
8773 i == Xsand_stonein_1 ? j + 1 :
8774 i == Xsand_stonein_2 ? j + 9 :
8775 i == Xsand_stonein_3 ? j + 17 :
8776 i == Xsand_stonein_4 ? j + 25 :
8777 i == Xsand_stoneout_1 && j == 0 ? 0 :
8778 i == Xsand_stoneout_1 && j == 1 ? 0 :
8779 i == Xsand_stoneout_1 && j == 2 ? 1 :
8780 i == Xsand_stoneout_1 && j == 3 ? 2 :
8781 i == Xsand_stoneout_1 && j == 4 ? 2 :
8782 i == Xsand_stoneout_1 && j == 5 ? 3 :
8783 i == Xsand_stoneout_1 && j == 6 ? 4 :
8784 i == Xsand_stoneout_1 && j == 7 ? 4 :
8785 i == Xsand_stoneout_2 && j == 0 ? 5 :
8786 i == Xsand_stoneout_2 && j == 1 ? 6 :
8787 i == Xsand_stoneout_2 && j == 2 ? 7 :
8788 i == Xsand_stoneout_2 && j == 3 ? 8 :
8789 i == Xsand_stoneout_2 && j == 4 ? 9 :
8790 i == Xsand_stoneout_2 && j == 5 ? 11 :
8791 i == Xsand_stoneout_2 && j == 6 ? 13 :
8792 i == Xsand_stoneout_2 && j == 7 ? 15 :
8793 i == Xboom_bug && j == 1 ? 2 :
8794 i == Xboom_bug && j == 2 ? 2 :
8795 i == Xboom_bug && j == 3 ? 4 :
8796 i == Xboom_bug && j == 4 ? 4 :
8797 i == Xboom_bug && j == 5 ? 2 :
8798 i == Xboom_bug && j == 6 ? 2 :
8799 i == Xboom_bug && j == 7 ? 0 :
8800 i == Xboom_bomb && j == 1 ? 2 :
8801 i == Xboom_bomb && j == 2 ? 2 :
8802 i == Xboom_bomb && j == 3 ? 4 :
8803 i == Xboom_bomb && j == 4 ? 4 :
8804 i == Xboom_bomb && j == 5 ? 2 :
8805 i == Xboom_bomb && j == 6 ? 2 :
8806 i == Xboom_bomb && j == 7 ? 0 :
8807 i == Xboom_android && j == 7 ? 6 :
8808 i == Xboom_1 && j == 1 ? 2 :
8809 i == Xboom_1 && j == 2 ? 2 :
8810 i == Xboom_1 && j == 3 ? 4 :
8811 i == Xboom_1 && j == 4 ? 4 :
8812 i == Xboom_1 && j == 5 ? 6 :
8813 i == Xboom_1 && j == 6 ? 6 :
8814 i == Xboom_1 && j == 7 ? 8 :
8815 i == Xboom_2 && j == 0 ? 8 :
8816 i == Xboom_2 && j == 1 ? 8 :
8817 i == Xboom_2 && j == 2 ? 10 :
8818 i == Xboom_2 && j == 3 ? 10 :
8819 i == Xboom_2 && j == 4 ? 10 :
8820 i == Xboom_2 && j == 5 ? 12 :
8821 i == Xboom_2 && j == 6 ? 12 :
8822 i == Xboom_2 && j == 7 ? 12 :
8823 special_animation && j == 4 ? 3 :
8824 effective_action != action ? 0 :
8828 Bitmap *debug_bitmap = g_em->bitmap;
8829 int debug_src_x = g_em->src_x;
8830 int debug_src_y = g_em->src_y;
8833 int frame = getAnimationFrame(g->anim_frames,
8836 g->anim_start_frame,
8839 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8840 g->double_movement && is_backside);
8842 g_em->bitmap = src_bitmap;
8843 g_em->src_x = src_x;
8844 g_em->src_y = src_y;
8845 g_em->src_offset_x = 0;
8846 g_em->src_offset_y = 0;
8847 g_em->dst_offset_x = 0;
8848 g_em->dst_offset_y = 0;
8849 g_em->width = TILEX;
8850 g_em->height = TILEY;
8852 g_em->preserve_background = FALSE;
8854 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8857 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8858 effective_action == ACTION_MOVING ||
8859 effective_action == ACTION_PUSHING ||
8860 effective_action == ACTION_EATING)) ||
8861 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8862 effective_action == ACTION_EMPTYING)))
8865 (effective_action == ACTION_FALLING ||
8866 effective_action == ACTION_FILLING ||
8867 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8868 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8869 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8870 int num_steps = (i == Ydrip_1_s ? 16 :
8871 i == Ydrip_1_sB ? 16 :
8872 i == Ydrip_2_s ? 16 :
8873 i == Ydrip_2_sB ? 16 :
8874 i == Xsand_stonein_1 ? 32 :
8875 i == Xsand_stonein_2 ? 32 :
8876 i == Xsand_stonein_3 ? 32 :
8877 i == Xsand_stonein_4 ? 32 :
8878 i == Xsand_stoneout_1 ? 16 :
8879 i == Xsand_stoneout_2 ? 16 : 8);
8880 int cx = ABS(dx) * (TILEX / num_steps);
8881 int cy = ABS(dy) * (TILEY / num_steps);
8882 int step_frame = (i == Ydrip_2_s ? j + 8 :
8883 i == Ydrip_2_sB ? j + 8 :
8884 i == Xsand_stonein_2 ? j + 8 :
8885 i == Xsand_stonein_3 ? j + 16 :
8886 i == Xsand_stonein_4 ? j + 24 :
8887 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8888 int step = (is_backside ? step_frame : num_steps - step_frame);
8890 if (is_backside) // tile where movement starts
8892 if (dx < 0 || dy < 0)
8894 g_em->src_offset_x = cx * step;
8895 g_em->src_offset_y = cy * step;
8899 g_em->dst_offset_x = cx * step;
8900 g_em->dst_offset_y = cy * step;
8903 else // tile where movement ends
8905 if (dx < 0 || dy < 0)
8907 g_em->dst_offset_x = cx * step;
8908 g_em->dst_offset_y = cy * step;
8912 g_em->src_offset_x = cx * step;
8913 g_em->src_offset_y = cy * step;
8917 g_em->width = TILEX - cx * step;
8918 g_em->height = TILEY - cy * step;
8921 // create unique graphic identifier to decide if tile must be redrawn
8922 /* bit 31 - 16 (16 bit): EM style graphic
8923 bit 15 - 12 ( 4 bit): EM style frame
8924 bit 11 - 6 ( 6 bit): graphic width
8925 bit 5 - 0 ( 6 bit): graphic height */
8926 g_em->unique_identifier =
8927 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8931 // skip check for EMC elements not contained in original EMC artwork
8932 if (element == EL_EMC_FAKE_ACID)
8935 if (g_em->bitmap != debug_bitmap ||
8936 g_em->src_x != debug_src_x ||
8937 g_em->src_y != debug_src_y ||
8938 g_em->src_offset_x != 0 ||
8939 g_em->src_offset_y != 0 ||
8940 g_em->dst_offset_x != 0 ||
8941 g_em->dst_offset_y != 0 ||
8942 g_em->width != TILEX ||
8943 g_em->height != TILEY)
8945 static int last_i = -1;
8953 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8954 i, element, element_info[element].token_name,
8955 element_action_info[effective_action].suffix, direction);
8957 if (element != effective_element)
8958 printf(" [%d ('%s')]",
8960 element_info[effective_element].token_name);
8964 if (g_em->bitmap != debug_bitmap)
8965 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8966 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8968 if (g_em->src_x != debug_src_x ||
8969 g_em->src_y != debug_src_y)
8970 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8971 j, (is_backside ? 'B' : 'F'),
8972 g_em->src_x, g_em->src_y,
8973 g_em->src_x / 32, g_em->src_y / 32,
8974 debug_src_x, debug_src_y,
8975 debug_src_x / 32, debug_src_y / 32);
8977 if (g_em->src_offset_x != 0 ||
8978 g_em->src_offset_y != 0 ||
8979 g_em->dst_offset_x != 0 ||
8980 g_em->dst_offset_y != 0)
8981 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8983 g_em->src_offset_x, g_em->src_offset_y,
8984 g_em->dst_offset_x, g_em->dst_offset_y);
8986 if (g_em->width != TILEX ||
8987 g_em->height != TILEY)
8988 printf(" %d (%d): size %d,%d should be %d,%d\n",
8990 g_em->width, g_em->height, TILEX, TILEY);
8992 num_em_gfx_errors++;
8999 for (i = 0; i < TILE_MAX; i++)
9001 for (j = 0; j < 8; j++)
9003 int element = object_mapping[i].element_rnd;
9004 int action = object_mapping[i].action;
9005 int direction = object_mapping[i].direction;
9006 boolean is_backside = object_mapping[i].is_backside;
9007 int graphic_action = el_act_dir2img(element, action, direction);
9008 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9010 if ((action == ACTION_SMASHED_BY_ROCK ||
9011 action == ACTION_SMASHED_BY_SPRING ||
9012 action == ACTION_EATING) &&
9013 graphic_action == graphic_default)
9015 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9016 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9017 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9018 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9021 // no separate animation for "smashed by rock" -- use rock instead
9022 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
9023 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
9025 g_em->bitmap = g_xx->bitmap;
9026 g_em->src_x = g_xx->src_x;
9027 g_em->src_y = g_xx->src_y;
9028 g_em->src_offset_x = g_xx->src_offset_x;
9029 g_em->src_offset_y = g_xx->src_offset_y;
9030 g_em->dst_offset_x = g_xx->dst_offset_x;
9031 g_em->dst_offset_y = g_xx->dst_offset_y;
9032 g_em->width = g_xx->width;
9033 g_em->height = g_xx->height;
9034 g_em->unique_identifier = g_xx->unique_identifier;
9037 g_em->preserve_background = TRUE;
9042 for (p = 0; p < MAX_PLAYERS; p++)
9044 for (i = 0; i < PLY_MAX; i++)
9046 int element = player_mapping[p][i].element_rnd;
9047 int action = player_mapping[p][i].action;
9048 int direction = player_mapping[p][i].direction;
9050 for (j = 0; j < 8; j++)
9052 int effective_element = element;
9053 int effective_action = action;
9054 int graphic = (direction == MV_NONE ?
9055 el_act2img(effective_element, effective_action) :
9056 el_act_dir2img(effective_element, effective_action,
9058 struct GraphicInfo *g = &graphic_info[graphic];
9059 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
9065 Bitmap *debug_bitmap = g_em->bitmap;
9066 int debug_src_x = g_em->src_x;
9067 int debug_src_y = g_em->src_y;
9070 int frame = getAnimationFrame(g->anim_frames,
9073 g->anim_start_frame,
9076 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9078 g_em->bitmap = src_bitmap;
9079 g_em->src_x = src_x;
9080 g_em->src_y = src_y;
9081 g_em->src_offset_x = 0;
9082 g_em->src_offset_y = 0;
9083 g_em->dst_offset_x = 0;
9084 g_em->dst_offset_y = 0;
9085 g_em->width = TILEX;
9086 g_em->height = TILEY;
9090 // skip check for EMC elements not contained in original EMC artwork
9091 if (element == EL_PLAYER_3 ||
9092 element == EL_PLAYER_4)
9095 if (g_em->bitmap != debug_bitmap ||
9096 g_em->src_x != debug_src_x ||
9097 g_em->src_y != debug_src_y)
9099 static int last_i = -1;
9107 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9108 p, i, element, element_info[element].token_name,
9109 element_action_info[effective_action].suffix, direction);
9111 if (element != effective_element)
9112 printf(" [%d ('%s')]",
9114 element_info[effective_element].token_name);
9118 if (g_em->bitmap != debug_bitmap)
9119 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9120 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9122 if (g_em->src_x != debug_src_x ||
9123 g_em->src_y != debug_src_y)
9124 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9126 g_em->src_x, g_em->src_y,
9127 g_em->src_x / 32, g_em->src_y / 32,
9128 debug_src_x, debug_src_y,
9129 debug_src_x / 32, debug_src_y / 32);
9131 num_em_gfx_errors++;
9141 printf("::: [%d errors found]\n", num_em_gfx_errors);
9147 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9148 boolean any_player_moving,
9149 boolean any_player_snapping,
9150 boolean any_player_dropping)
9152 if (frame == 0 && !any_player_dropping)
9154 if (!local_player->was_waiting)
9156 if (!CheckSaveEngineSnapshotToList())
9159 local_player->was_waiting = TRUE;
9162 else if (any_player_moving || any_player_snapping || any_player_dropping)
9164 local_player->was_waiting = FALSE;
9168 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9169 boolean murphy_is_dropping)
9171 if (murphy_is_waiting)
9173 if (!local_player->was_waiting)
9175 if (!CheckSaveEngineSnapshotToList())
9178 local_player->was_waiting = TRUE;
9183 local_player->was_waiting = FALSE;
9187 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9188 boolean button_released)
9190 if (button_released)
9192 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9193 CheckSaveEngineSnapshotToList();
9195 else if (element_clicked)
9197 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9198 CheckSaveEngineSnapshotToList();
9200 game.snapshot.changed_action = TRUE;
9204 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9205 boolean any_player_moving,
9206 boolean any_player_snapping,
9207 boolean any_player_dropping)
9209 if (tape.single_step && tape.recording && !tape.pausing)
9210 if (frame == 0 && !any_player_dropping)
9211 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9213 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9214 any_player_snapping, any_player_dropping);
9217 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9218 boolean murphy_is_dropping)
9220 boolean murphy_starts_dropping = FALSE;
9223 for (i = 0; i < MAX_PLAYERS; i++)
9224 if (stored_player[i].force_dropping)
9225 murphy_starts_dropping = TRUE;
9227 if (tape.single_step && tape.recording && !tape.pausing)
9228 if (murphy_is_waiting && !murphy_starts_dropping)
9229 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9231 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9234 void CheckSingleStepMode_MM(boolean element_clicked,
9235 boolean button_released)
9237 if (tape.single_step && tape.recording && !tape.pausing)
9238 if (button_released)
9239 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9241 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9244 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9245 int graphic, int sync_frame, int x, int y)
9247 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9249 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9252 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9254 return (IS_NEXT_FRAME(sync_frame, graphic));
9257 int getGraphicInfo_Delay(int graphic)
9259 return graphic_info[graphic].anim_delay;
9262 void PlayMenuSoundExt(int sound)
9264 if (sound == SND_UNDEFINED)
9267 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9268 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9271 if (IS_LOOP_SOUND(sound))
9272 PlaySoundLoop(sound);
9277 void PlayMenuSound(void)
9279 PlayMenuSoundExt(menu.sound[game_status]);
9282 void PlayMenuSoundStereo(int sound, int stereo_position)
9284 if (sound == SND_UNDEFINED)
9287 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9288 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9291 if (IS_LOOP_SOUND(sound))
9292 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9294 PlaySoundStereo(sound, stereo_position);
9297 void PlayMenuSoundIfLoopExt(int sound)
9299 if (sound == SND_UNDEFINED)
9302 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9303 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9306 if (IS_LOOP_SOUND(sound))
9307 PlaySoundLoop(sound);
9310 void PlayMenuSoundIfLoop(void)
9312 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9315 void PlayMenuMusicExt(int music)
9317 if (music == MUS_UNDEFINED)
9320 if (!setup.sound_music)
9323 if (IS_LOOP_MUSIC(music))
9324 PlayMusicLoop(music);
9329 void PlayMenuMusic(void)
9331 char *curr_music = getCurrentlyPlayingMusicFilename();
9332 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9334 if (!strEqual(curr_music, next_music))
9335 PlayMenuMusicExt(menu.music[game_status]);
9338 void PlayMenuSoundsAndMusic(void)
9344 static void FadeMenuSounds(void)
9349 static void FadeMenuMusic(void)
9351 char *curr_music = getCurrentlyPlayingMusicFilename();
9352 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9354 if (!strEqual(curr_music, next_music))
9358 void FadeMenuSoundsAndMusic(void)
9364 void PlaySoundActivating(void)
9367 PlaySound(SND_MENU_ITEM_ACTIVATING);
9371 void PlaySoundSelecting(void)
9374 PlaySound(SND_MENU_ITEM_SELECTING);
9378 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9380 boolean change_fullscreen = (setup.fullscreen !=
9381 video.fullscreen_enabled);
9382 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9383 setup.window_scaling_percent !=
9384 video.window_scaling_percent);
9386 if (change_window_scaling_percent && video.fullscreen_enabled)
9389 if (!change_window_scaling_percent && !video.fullscreen_available)
9392 if (change_window_scaling_percent)
9394 SDLSetWindowScaling(setup.window_scaling_percent);
9398 else if (change_fullscreen)
9400 SDLSetWindowFullscreen(setup.fullscreen);
9402 // set setup value according to successfully changed fullscreen mode
9403 setup.fullscreen = video.fullscreen_enabled;
9408 if (change_fullscreen ||
9409 change_window_scaling_percent)
9411 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9413 // save backbuffer content which gets lost when toggling fullscreen mode
9414 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9416 if (change_window_scaling_percent)
9418 // keep window mode, but change window scaling
9419 video.fullscreen_enabled = TRUE; // force new window scaling
9422 // toggle fullscreen
9423 ChangeVideoModeIfNeeded(setup.fullscreen);
9425 // set setup value according to successfully changed fullscreen mode
9426 setup.fullscreen = video.fullscreen_enabled;
9428 // restore backbuffer content from temporary backbuffer backup bitmap
9429 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9431 FreeBitmap(tmp_backbuffer);
9433 // update visible window/screen
9434 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9438 static void JoinRectangles(int *x, int *y, int *width, int *height,
9439 int x2, int y2, int width2, int height2)
9441 // do not join with "off-screen" rectangle
9442 if (x2 == -1 || y2 == -1)
9447 *width = MAX(*width, width2);
9448 *height = MAX(*height, height2);
9451 void SetAnimStatus(int anim_status_new)
9453 if (anim_status_new == GAME_MODE_MAIN)
9454 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9455 else if (anim_status_new == GAME_MODE_SCORES)
9456 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9458 global.anim_status_next = anim_status_new;
9460 // directly set screen modes that are entered without fading
9461 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9462 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9463 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9464 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9465 global.anim_status = global.anim_status_next;
9468 void SetGameStatus(int game_status_new)
9470 if (game_status_new != game_status)
9471 game_status_last_screen = game_status;
9473 game_status = game_status_new;
9475 SetAnimStatus(game_status_new);
9478 void SetFontStatus(int game_status_new)
9480 static int last_game_status = -1;
9482 if (game_status_new != -1)
9484 // set game status for font use after storing last game status
9485 last_game_status = game_status;
9486 game_status = game_status_new;
9490 // reset game status after font use from last stored game status
9491 game_status = last_game_status;
9495 void ResetFontStatus(void)
9500 void SetLevelSetInfo(char *identifier, int level_nr)
9502 setString(&levelset.identifier, identifier);
9504 levelset.level_nr = level_nr;
9507 boolean CheckIfAllViewportsHaveChanged(void)
9509 // if game status has not changed, viewports have not changed either
9510 if (game_status == game_status_last)
9513 // check if all viewports have changed with current game status
9515 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9516 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9517 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9518 int new_real_sx = vp_playfield->x;
9519 int new_real_sy = vp_playfield->y;
9520 int new_full_sxsize = vp_playfield->width;
9521 int new_full_sysize = vp_playfield->height;
9522 int new_dx = vp_door_1->x;
9523 int new_dy = vp_door_1->y;
9524 int new_dxsize = vp_door_1->width;
9525 int new_dysize = vp_door_1->height;
9526 int new_vx = vp_door_2->x;
9527 int new_vy = vp_door_2->y;
9528 int new_vxsize = vp_door_2->width;
9529 int new_vysize = vp_door_2->height;
9531 boolean playfield_viewport_has_changed =
9532 (new_real_sx != REAL_SX ||
9533 new_real_sy != REAL_SY ||
9534 new_full_sxsize != FULL_SXSIZE ||
9535 new_full_sysize != FULL_SYSIZE);
9537 boolean door_1_viewport_has_changed =
9540 new_dxsize != DXSIZE ||
9541 new_dysize != DYSIZE);
9543 boolean door_2_viewport_has_changed =
9546 new_vxsize != VXSIZE ||
9547 new_vysize != VYSIZE ||
9548 game_status_last == GAME_MODE_EDITOR);
9550 return (playfield_viewport_has_changed &&
9551 door_1_viewport_has_changed &&
9552 door_2_viewport_has_changed);
9555 boolean CheckFadeAll(void)
9557 return (CheckIfGlobalBorderHasChanged() ||
9558 CheckIfAllViewportsHaveChanged());
9561 void ChangeViewportPropertiesIfNeeded(void)
9563 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9564 FALSE : setup.small_game_graphics);
9565 int gfx_game_mode = game_status;
9566 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9568 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9569 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9570 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9571 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9572 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9573 int new_win_xsize = vp_window->width;
9574 int new_win_ysize = vp_window->height;
9575 int border_left = vp_playfield->border_left;
9576 int border_right = vp_playfield->border_right;
9577 int border_top = vp_playfield->border_top;
9578 int border_bottom = vp_playfield->border_bottom;
9579 int new_sx = vp_playfield->x + border_left;
9580 int new_sy = vp_playfield->y + border_top;
9581 int new_sxsize = vp_playfield->width - border_left - border_right;
9582 int new_sysize = vp_playfield->height - border_top - border_bottom;
9583 int new_real_sx = vp_playfield->x;
9584 int new_real_sy = vp_playfield->y;
9585 int new_full_sxsize = vp_playfield->width;
9586 int new_full_sysize = vp_playfield->height;
9587 int new_dx = vp_door_1->x;
9588 int new_dy = vp_door_1->y;
9589 int new_dxsize = vp_door_1->width;
9590 int new_dysize = vp_door_1->height;
9591 int new_vx = vp_door_2->x;
9592 int new_vy = vp_door_2->y;
9593 int new_vxsize = vp_door_2->width;
9594 int new_vysize = vp_door_2->height;
9595 int new_ex = vp_door_3->x;
9596 int new_ey = vp_door_3->y;
9597 int new_exsize = vp_door_3->width;
9598 int new_eysize = vp_door_3->height;
9599 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9600 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9601 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9602 int new_scr_fieldx = new_sxsize / tilesize;
9603 int new_scr_fieldy = new_sysize / tilesize;
9604 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9605 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9606 boolean init_gfx_buffers = FALSE;
9607 boolean init_video_buffer = FALSE;
9608 boolean init_gadgets_and_anims = FALSE;
9609 boolean init_em_graphics = FALSE;
9611 if (new_win_xsize != WIN_XSIZE ||
9612 new_win_ysize != WIN_YSIZE)
9614 WIN_XSIZE = new_win_xsize;
9615 WIN_YSIZE = new_win_ysize;
9617 init_video_buffer = TRUE;
9618 init_gfx_buffers = TRUE;
9619 init_gadgets_and_anims = TRUE;
9621 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9624 if (new_scr_fieldx != SCR_FIELDX ||
9625 new_scr_fieldy != SCR_FIELDY)
9627 // this always toggles between MAIN and GAME when using small tile size
9629 SCR_FIELDX = new_scr_fieldx;
9630 SCR_FIELDY = new_scr_fieldy;
9632 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9643 new_sxsize != SXSIZE ||
9644 new_sysize != SYSIZE ||
9645 new_dxsize != DXSIZE ||
9646 new_dysize != DYSIZE ||
9647 new_vxsize != VXSIZE ||
9648 new_vysize != VYSIZE ||
9649 new_exsize != EXSIZE ||
9650 new_eysize != EYSIZE ||
9651 new_real_sx != REAL_SX ||
9652 new_real_sy != REAL_SY ||
9653 new_full_sxsize != FULL_SXSIZE ||
9654 new_full_sysize != FULL_SYSIZE ||
9655 new_tilesize_var != TILESIZE_VAR
9658 // ------------------------------------------------------------------------
9659 // determine next fading area for changed viewport definitions
9660 // ------------------------------------------------------------------------
9662 // start with current playfield area (default fading area)
9665 FADE_SXSIZE = FULL_SXSIZE;
9666 FADE_SYSIZE = FULL_SYSIZE;
9668 // add new playfield area if position or size has changed
9669 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9670 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9672 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9673 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9676 // add current and new door 1 area if position or size has changed
9677 if (new_dx != DX || new_dy != DY ||
9678 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9680 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9681 DX, DY, DXSIZE, DYSIZE);
9682 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9683 new_dx, new_dy, new_dxsize, new_dysize);
9686 // add current and new door 2 area if position or size has changed
9687 if (new_vx != VX || new_vy != VY ||
9688 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9690 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9691 VX, VY, VXSIZE, VYSIZE);
9692 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9693 new_vx, new_vy, new_vxsize, new_vysize);
9696 // ------------------------------------------------------------------------
9697 // handle changed tile size
9698 // ------------------------------------------------------------------------
9700 if (new_tilesize_var != TILESIZE_VAR)
9702 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9704 // changing tile size invalidates scroll values of engine snapshots
9705 FreeEngineSnapshotSingle();
9707 // changing tile size requires update of graphic mapping for EM engine
9708 init_em_graphics = TRUE;
9719 SXSIZE = new_sxsize;
9720 SYSIZE = new_sysize;
9721 DXSIZE = new_dxsize;
9722 DYSIZE = new_dysize;
9723 VXSIZE = new_vxsize;
9724 VYSIZE = new_vysize;
9725 EXSIZE = new_exsize;
9726 EYSIZE = new_eysize;
9727 REAL_SX = new_real_sx;
9728 REAL_SY = new_real_sy;
9729 FULL_SXSIZE = new_full_sxsize;
9730 FULL_SYSIZE = new_full_sysize;
9731 TILESIZE_VAR = new_tilesize_var;
9733 init_gfx_buffers = TRUE;
9734 init_gadgets_and_anims = TRUE;
9736 // printf("::: viewports: init_gfx_buffers\n");
9737 // printf("::: viewports: init_gadgets_and_anims\n");
9740 if (init_gfx_buffers)
9742 // printf("::: init_gfx_buffers\n");
9744 SCR_FIELDX = new_scr_fieldx_buffers;
9745 SCR_FIELDY = new_scr_fieldy_buffers;
9749 SCR_FIELDX = new_scr_fieldx;
9750 SCR_FIELDY = new_scr_fieldy;
9752 SetDrawDeactivationMask(REDRAW_NONE);
9753 SetDrawBackgroundMask(REDRAW_FIELD);
9756 if (init_video_buffer)
9758 // printf("::: init_video_buffer\n");
9760 FreeAllImageTextures(); // needs old renderer to free the textures
9762 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9763 InitImageTextures();
9766 if (init_gadgets_and_anims)
9768 // printf("::: init_gadgets_and_anims\n");
9771 InitGlobalAnimations();
9774 if (init_em_graphics)
9776 InitGraphicInfo_EM();