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 Xwall_1, TRUE, FALSE,
7093 Xwall_2, TRUE, FALSE,
7094 EL_EMC_WALL_14, -1, -1
7097 Xwall_3, TRUE, FALSE,
7098 EL_EMC_WALL_15, -1, -1
7101 Xwall_4, TRUE, FALSE,
7102 EL_EMC_WALL_16, -1, -1
7106 Xroundwall_1, TRUE, FALSE,
7107 EL_WALL_SLIPPERY, -1, -1
7110 Xroundwall_2, TRUE, FALSE,
7111 EL_EMC_WALL_SLIPPERY_2, -1, -1
7114 Xroundwall_3, TRUE, FALSE,
7115 EL_EMC_WALL_SLIPPERY_3, -1, -1
7118 Xroundwall_4, TRUE, FALSE,
7119 EL_EMC_WALL_SLIPPERY_4, -1, -1
7123 Xsteel_1, TRUE, FALSE,
7124 EL_STEELWALL, -1, -1
7127 Xsteel_2, TRUE, FALSE,
7128 EL_EMC_STEELWALL_2, -1, -1
7131 Xsteel_3, TRUE, FALSE,
7132 EL_EMC_STEELWALL_3, -1, -1
7135 Xsteel_4, TRUE, FALSE,
7136 EL_EMC_STEELWALL_4, -1, -1
7140 Xdecor_1, TRUE, FALSE,
7141 EL_EMC_WALL_8, -1, -1
7144 Xdecor_2, TRUE, FALSE,
7145 EL_EMC_WALL_6, -1, -1
7148 Xdecor_3, TRUE, FALSE,
7149 EL_EMC_WALL_4, -1, -1
7152 Xdecor_4, TRUE, FALSE,
7153 EL_EMC_WALL_7, -1, -1
7156 Xdecor_5, TRUE, FALSE,
7157 EL_EMC_WALL_5, -1, -1
7160 Xdecor_6, TRUE, FALSE,
7161 EL_EMC_WALL_9, -1, -1
7164 Xdecor_7, TRUE, FALSE,
7165 EL_EMC_WALL_10, -1, -1
7168 Xdecor_8, TRUE, FALSE,
7169 EL_EMC_WALL_1, -1, -1
7172 Xdecor_9, TRUE, FALSE,
7173 EL_EMC_WALL_2, -1, -1
7176 Xdecor_10, TRUE, FALSE,
7177 EL_EMC_WALL_3, -1, -1
7180 Xdecor_11, TRUE, FALSE,
7181 EL_EMC_WALL_11, -1, -1
7184 Xdecor_12, TRUE, FALSE,
7185 EL_EMC_WALL_12, -1, -1
7189 Xalpha_0, TRUE, FALSE,
7190 EL_CHAR('0'), -1, -1
7193 Xalpha_1, TRUE, FALSE,
7194 EL_CHAR('1'), -1, -1
7197 Xalpha_2, TRUE, FALSE,
7198 EL_CHAR('2'), -1, -1
7201 Xalpha_3, TRUE, FALSE,
7202 EL_CHAR('3'), -1, -1
7205 Xalpha_4, TRUE, FALSE,
7206 EL_CHAR('4'), -1, -1
7209 Xalpha_5, TRUE, FALSE,
7210 EL_CHAR('5'), -1, -1
7213 Xalpha_6, TRUE, FALSE,
7214 EL_CHAR('6'), -1, -1
7217 Xalpha_7, TRUE, FALSE,
7218 EL_CHAR('7'), -1, -1
7221 Xalpha_8, TRUE, FALSE,
7222 EL_CHAR('8'), -1, -1
7225 Xalpha_9, TRUE, FALSE,
7226 EL_CHAR('9'), -1, -1
7229 Xalpha_excla, TRUE, FALSE,
7230 EL_CHAR('!'), -1, -1
7233 Xalpha_quote, TRUE, FALSE,
7234 EL_CHAR('"'), -1, -1
7237 Xalpha_comma, TRUE, FALSE,
7238 EL_CHAR(','), -1, -1
7241 Xalpha_minus, TRUE, FALSE,
7242 EL_CHAR('-'), -1, -1
7245 Xalpha_perio, TRUE, FALSE,
7246 EL_CHAR('.'), -1, -1
7249 Xalpha_colon, TRUE, FALSE,
7250 EL_CHAR(':'), -1, -1
7253 Xalpha_quest, TRUE, FALSE,
7254 EL_CHAR('?'), -1, -1
7257 Xalpha_a, TRUE, FALSE,
7258 EL_CHAR('A'), -1, -1
7261 Xalpha_b, TRUE, FALSE,
7262 EL_CHAR('B'), -1, -1
7265 Xalpha_c, TRUE, FALSE,
7266 EL_CHAR('C'), -1, -1
7269 Xalpha_d, TRUE, FALSE,
7270 EL_CHAR('D'), -1, -1
7273 Xalpha_e, TRUE, FALSE,
7274 EL_CHAR('E'), -1, -1
7277 Xalpha_f, TRUE, FALSE,
7278 EL_CHAR('F'), -1, -1
7281 Xalpha_g, TRUE, FALSE,
7282 EL_CHAR('G'), -1, -1
7285 Xalpha_h, TRUE, FALSE,
7286 EL_CHAR('H'), -1, -1
7289 Xalpha_i, TRUE, FALSE,
7290 EL_CHAR('I'), -1, -1
7293 Xalpha_j, TRUE, FALSE,
7294 EL_CHAR('J'), -1, -1
7297 Xalpha_k, TRUE, FALSE,
7298 EL_CHAR('K'), -1, -1
7301 Xalpha_l, TRUE, FALSE,
7302 EL_CHAR('L'), -1, -1
7305 Xalpha_m, TRUE, FALSE,
7306 EL_CHAR('M'), -1, -1
7309 Xalpha_n, TRUE, FALSE,
7310 EL_CHAR('N'), -1, -1
7313 Xalpha_o, TRUE, FALSE,
7314 EL_CHAR('O'), -1, -1
7317 Xalpha_p, TRUE, FALSE,
7318 EL_CHAR('P'), -1, -1
7321 Xalpha_q, TRUE, FALSE,
7322 EL_CHAR('Q'), -1, -1
7325 Xalpha_r, TRUE, FALSE,
7326 EL_CHAR('R'), -1, -1
7329 Xalpha_s, TRUE, FALSE,
7330 EL_CHAR('S'), -1, -1
7333 Xalpha_t, TRUE, FALSE,
7334 EL_CHAR('T'), -1, -1
7337 Xalpha_u, TRUE, FALSE,
7338 EL_CHAR('U'), -1, -1
7341 Xalpha_v, TRUE, FALSE,
7342 EL_CHAR('V'), -1, -1
7345 Xalpha_w, TRUE, FALSE,
7346 EL_CHAR('W'), -1, -1
7349 Xalpha_x, TRUE, FALSE,
7350 EL_CHAR('X'), -1, -1
7353 Xalpha_y, TRUE, FALSE,
7354 EL_CHAR('Y'), -1, -1
7357 Xalpha_z, TRUE, FALSE,
7358 EL_CHAR('Z'), -1, -1
7361 Xalpha_arrow_e, TRUE, FALSE,
7362 EL_CHAR('>'), -1, -1
7365 Xalpha_arrow_w, TRUE, FALSE,
7366 EL_CHAR('<'), -1, -1
7369 Xalpha_copyr, TRUE, FALSE,
7370 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7374 Ykey_1_blank, FALSE, FALSE,
7375 EL_EM_KEY_1, ACTION_COLLECTING, -1
7378 Ykey_2_blank, FALSE, FALSE,
7379 EL_EM_KEY_2, ACTION_COLLECTING, -1
7382 Ykey_3_blank, FALSE, FALSE,
7383 EL_EM_KEY_3, ACTION_COLLECTING, -1
7386 Ykey_4_blank, FALSE, FALSE,
7387 EL_EM_KEY_4, ACTION_COLLECTING, -1
7390 Ykey_5_blank, FALSE, FALSE,
7391 EL_EMC_KEY_5, ACTION_COLLECTING, -1
7394 Ykey_6_blank, FALSE, FALSE,
7395 EL_EMC_KEY_6, ACTION_COLLECTING, -1
7398 Ykey_7_blank, FALSE, FALSE,
7399 EL_EMC_KEY_7, ACTION_COLLECTING, -1
7402 Ykey_8_blank, FALSE, FALSE,
7403 EL_EMC_KEY_8, ACTION_COLLECTING, -1
7406 Ylenses_blank, FALSE, FALSE,
7407 EL_EMC_LENSES, ACTION_COLLECTING, -1
7410 Ymagnify_blank, FALSE, FALSE,
7411 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
7414 Ygrass_blank, FALSE, FALSE,
7415 EL_EMC_GRASS, ACTION_SNAPPING, -1
7418 Ydirt_blank, FALSE, FALSE,
7419 EL_SAND, ACTION_SNAPPING, -1
7423 Xboom_bug, FALSE, FALSE,
7424 EL_BUG, ACTION_EXPLODING, -1
7427 Xboom_bomb, FALSE, FALSE,
7428 EL_BOMB, ACTION_EXPLODING, -1
7431 Xboom_android, FALSE, FALSE,
7432 EL_EMC_ANDROID, ACTION_OTHER, -1
7435 Xboom_1, FALSE, FALSE,
7436 EL_DEFAULT, ACTION_EXPLODING, -1
7439 Xboom_2, FALSE, FALSE,
7440 EL_DEFAULT, ACTION_EXPLODING, -1
7443 Znormal, FALSE, FALSE,
7447 Zdynamite, FALSE, FALSE,
7451 Zplayer, FALSE, FALSE,
7455 Zborder, FALSE, FALSE,
7465 static struct Mapping_EM_to_RND_player
7474 em_player_mapping_list[] =
7478 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7482 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7486 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7490 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7494 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7498 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7502 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7506 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7510 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7514 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7518 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7522 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7526 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7530 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7534 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7538 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7542 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7546 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7550 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7554 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7558 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7562 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7566 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7570 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7574 EL_PLAYER_1, ACTION_DEFAULT, -1,
7578 EL_PLAYER_2, ACTION_DEFAULT, -1,
7582 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7586 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7590 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7594 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7598 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7602 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7606 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7610 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7614 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7618 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7622 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7626 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7630 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7634 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7638 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7642 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7646 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7650 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7654 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7658 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7662 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7666 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7670 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7674 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7678 EL_PLAYER_3, ACTION_DEFAULT, -1,
7682 EL_PLAYER_4, ACTION_DEFAULT, -1,
7691 int map_element_RND_to_EM(int element_rnd)
7693 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7694 static boolean mapping_initialized = FALSE;
7696 if (!mapping_initialized)
7700 // return "Xalpha_quest" for all undefined elements in mapping array
7701 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7702 mapping_RND_to_EM[i] = Xalpha_quest;
7704 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7705 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7706 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7707 em_object_mapping_list[i].element_em;
7709 mapping_initialized = TRUE;
7712 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7713 return mapping_RND_to_EM[element_rnd];
7715 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7720 int map_element_EM_to_RND(int element_em)
7722 static unsigned short mapping_EM_to_RND[TILE_MAX];
7723 static boolean mapping_initialized = FALSE;
7725 if (!mapping_initialized)
7729 // return "EL_UNKNOWN" for all undefined elements in mapping array
7730 for (i = 0; i < TILE_MAX; i++)
7731 mapping_EM_to_RND[i] = EL_UNKNOWN;
7733 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7734 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7735 em_object_mapping_list[i].element_rnd;
7737 mapping_initialized = TRUE;
7740 if (element_em >= 0 && element_em < TILE_MAX)
7741 return mapping_EM_to_RND[element_em];
7743 Error(ERR_WARN, "invalid EM level element %d", element_em);
7748 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7750 struct LevelInfo_EM *level_em = level->native_em_level;
7751 struct LEVEL *lev = level_em->lev;
7754 for (i = 0; i < TILE_MAX; i++)
7755 lev->android_array[i] = Xblank;
7757 for (i = 0; i < level->num_android_clone_elements; i++)
7759 int element_rnd = level->android_clone_element[i];
7760 int element_em = map_element_RND_to_EM(element_rnd);
7762 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7763 if (em_object_mapping_list[j].element_rnd == element_rnd)
7764 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7768 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7770 struct LevelInfo_EM *level_em = level->native_em_level;
7771 struct LEVEL *lev = level_em->lev;
7774 level->num_android_clone_elements = 0;
7776 for (i = 0; i < TILE_MAX; i++)
7778 int element_em = lev->android_array[i];
7780 boolean element_found = FALSE;
7782 if (element_em == Xblank)
7785 element_rnd = map_element_EM_to_RND(element_em);
7787 for (j = 0; j < level->num_android_clone_elements; j++)
7788 if (level->android_clone_element[j] == element_rnd)
7789 element_found = TRUE;
7793 level->android_clone_element[level->num_android_clone_elements++] =
7796 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7801 if (level->num_android_clone_elements == 0)
7803 level->num_android_clone_elements = 1;
7804 level->android_clone_element[0] = EL_EMPTY;
7808 int map_direction_RND_to_EM(int direction)
7810 return (direction == MV_UP ? 0 :
7811 direction == MV_RIGHT ? 1 :
7812 direction == MV_DOWN ? 2 :
7813 direction == MV_LEFT ? 3 :
7817 int map_direction_EM_to_RND(int direction)
7819 return (direction == 0 ? MV_UP :
7820 direction == 1 ? MV_RIGHT :
7821 direction == 2 ? MV_DOWN :
7822 direction == 3 ? MV_LEFT :
7826 int map_element_RND_to_SP(int element_rnd)
7828 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7830 if (element_rnd >= EL_SP_START &&
7831 element_rnd <= EL_SP_END)
7832 element_sp = element_rnd - EL_SP_START;
7833 else if (element_rnd == EL_EMPTY_SPACE)
7835 else if (element_rnd == EL_INVISIBLE_WALL)
7841 int map_element_SP_to_RND(int element_sp)
7843 int element_rnd = EL_UNKNOWN;
7845 if (element_sp >= 0x00 &&
7847 element_rnd = EL_SP_START + element_sp;
7848 else if (element_sp == 0x28)
7849 element_rnd = EL_INVISIBLE_WALL;
7854 int map_action_SP_to_RND(int action_sp)
7858 case actActive: return ACTION_ACTIVE;
7859 case actImpact: return ACTION_IMPACT;
7860 case actExploding: return ACTION_EXPLODING;
7861 case actDigging: return ACTION_DIGGING;
7862 case actSnapping: return ACTION_SNAPPING;
7863 case actCollecting: return ACTION_COLLECTING;
7864 case actPassing: return ACTION_PASSING;
7865 case actPushing: return ACTION_PUSHING;
7866 case actDropping: return ACTION_DROPPING;
7868 default: return ACTION_DEFAULT;
7872 int map_element_RND_to_MM(int element_rnd)
7874 return (element_rnd >= EL_MM_START_1 &&
7875 element_rnd <= EL_MM_END_1 ?
7876 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7878 element_rnd >= EL_MM_START_2 &&
7879 element_rnd <= EL_MM_END_2 ?
7880 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7882 element_rnd >= EL_CHAR_START &&
7883 element_rnd <= EL_CHAR_END ?
7884 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7886 element_rnd >= EL_MM_RUNTIME_START &&
7887 element_rnd <= EL_MM_RUNTIME_END ?
7888 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7890 element_rnd >= EL_MM_DUMMY_START &&
7891 element_rnd <= EL_MM_DUMMY_END ?
7892 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7894 EL_MM_EMPTY_NATIVE);
7897 int map_element_MM_to_RND(int element_mm)
7899 return (element_mm == EL_MM_EMPTY_NATIVE ||
7900 element_mm == EL_DF_EMPTY_NATIVE ?
7903 element_mm >= EL_MM_START_1_NATIVE &&
7904 element_mm <= EL_MM_END_1_NATIVE ?
7905 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7907 element_mm >= EL_MM_START_2_NATIVE &&
7908 element_mm <= EL_MM_END_2_NATIVE ?
7909 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7911 element_mm >= EL_MM_CHAR_START_NATIVE &&
7912 element_mm <= EL_MM_CHAR_END_NATIVE ?
7913 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7915 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7916 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7917 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7919 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7920 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7921 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7926 int map_action_MM_to_RND(int action_mm)
7928 // all MM actions are defined to exactly match their RND counterparts
7932 int map_sound_MM_to_RND(int sound_mm)
7936 case SND_MM_GAME_LEVELTIME_CHARGING:
7937 return SND_GAME_LEVELTIME_CHARGING;
7939 case SND_MM_GAME_HEALTH_CHARGING:
7940 return SND_GAME_HEALTH_CHARGING;
7943 return SND_UNDEFINED;
7947 int map_mm_wall_element(int element)
7949 return (element >= EL_MM_STEEL_WALL_START &&
7950 element <= EL_MM_STEEL_WALL_END ?
7953 element >= EL_MM_WOODEN_WALL_START &&
7954 element <= EL_MM_WOODEN_WALL_END ?
7957 element >= EL_MM_ICE_WALL_START &&
7958 element <= EL_MM_ICE_WALL_END ?
7961 element >= EL_MM_AMOEBA_WALL_START &&
7962 element <= EL_MM_AMOEBA_WALL_END ?
7965 element >= EL_DF_STEEL_WALL_START &&
7966 element <= EL_DF_STEEL_WALL_END ?
7969 element >= EL_DF_WOODEN_WALL_START &&
7970 element <= EL_DF_WOODEN_WALL_END ?
7976 int map_mm_wall_element_editor(int element)
7980 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7981 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7982 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7983 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7984 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7985 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7987 default: return element;
7991 int get_next_element(int element)
7995 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7996 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7997 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7998 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7999 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
8000 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
8001 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
8002 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
8003 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
8004 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
8005 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
8007 default: return element;
8011 int el2img_mm(int element_mm)
8013 return el2img(map_element_MM_to_RND(element_mm));
8016 int el_act_dir2img(int element, int action, int direction)
8018 element = GFX_ELEMENT(element);
8019 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8021 // direction_graphic[][] == graphic[] for undefined direction graphics
8022 return element_info[element].direction_graphic[action][direction];
8025 static int el_act_dir2crm(int element, int action, int direction)
8027 element = GFX_ELEMENT(element);
8028 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
8030 // direction_graphic[][] == graphic[] for undefined direction graphics
8031 return element_info[element].direction_crumbled[action][direction];
8034 int el_act2img(int element, int action)
8036 element = GFX_ELEMENT(element);
8038 return element_info[element].graphic[action];
8041 int el_act2crm(int element, int action)
8043 element = GFX_ELEMENT(element);
8045 return element_info[element].crumbled[action];
8048 int el_dir2img(int element, int direction)
8050 element = GFX_ELEMENT(element);
8052 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8055 int el2baseimg(int element)
8057 return element_info[element].graphic[ACTION_DEFAULT];
8060 int el2img(int element)
8062 element = GFX_ELEMENT(element);
8064 return element_info[element].graphic[ACTION_DEFAULT];
8067 int el2edimg(int element)
8069 element = GFX_ELEMENT(element);
8071 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8074 int el2preimg(int element)
8076 element = GFX_ELEMENT(element);
8078 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8081 int el2panelimg(int element)
8083 element = GFX_ELEMENT(element);
8085 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8088 int font2baseimg(int font_nr)
8090 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8093 int getBeltNrFromBeltElement(int element)
8095 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8096 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8097 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8100 int getBeltNrFromBeltActiveElement(int element)
8102 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8103 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8104 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8107 int getBeltNrFromBeltSwitchElement(int element)
8109 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8110 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8111 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8114 int getBeltDirNrFromBeltElement(int element)
8116 static int belt_base_element[4] =
8118 EL_CONVEYOR_BELT_1_LEFT,
8119 EL_CONVEYOR_BELT_2_LEFT,
8120 EL_CONVEYOR_BELT_3_LEFT,
8121 EL_CONVEYOR_BELT_4_LEFT
8124 int belt_nr = getBeltNrFromBeltElement(element);
8125 int belt_dir_nr = element - belt_base_element[belt_nr];
8127 return (belt_dir_nr % 3);
8130 int getBeltDirNrFromBeltSwitchElement(int element)
8132 static int belt_base_element[4] =
8134 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8135 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8136 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8137 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8140 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8141 int belt_dir_nr = element - belt_base_element[belt_nr];
8143 return (belt_dir_nr % 3);
8146 int getBeltDirFromBeltElement(int element)
8148 static int belt_move_dir[3] =
8155 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8157 return belt_move_dir[belt_dir_nr];
8160 int getBeltDirFromBeltSwitchElement(int element)
8162 static int belt_move_dir[3] =
8169 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8171 return belt_move_dir[belt_dir_nr];
8174 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8176 static int belt_base_element[4] =
8178 EL_CONVEYOR_BELT_1_LEFT,
8179 EL_CONVEYOR_BELT_2_LEFT,
8180 EL_CONVEYOR_BELT_3_LEFT,
8181 EL_CONVEYOR_BELT_4_LEFT
8184 return belt_base_element[belt_nr] + belt_dir_nr;
8187 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8189 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8191 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8194 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8196 static int belt_base_element[4] =
8198 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8199 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8200 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8201 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8204 return belt_base_element[belt_nr] + belt_dir_nr;
8207 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8209 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8211 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8214 boolean getTeamMode_EM(void)
8216 return game.team_mode || network_playing;
8219 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8221 int game_frame_delay_value;
8223 game_frame_delay_value =
8224 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8225 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8228 if (tape.playing && tape.warp_forward && !tape.pausing)
8229 game_frame_delay_value = 0;
8231 return game_frame_delay_value;
8234 unsigned int InitRND(int seed)
8236 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8237 return InitEngineRandom_EM(seed);
8238 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8239 return InitEngineRandom_SP(seed);
8240 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8241 return InitEngineRandom_MM(seed);
8243 return InitEngineRandom_RND(seed);
8246 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8247 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8249 static int get_effective_element_EM(int tile, int frame_em)
8251 int element = object_mapping[tile].element_rnd;
8252 int action = object_mapping[tile].action;
8253 boolean is_backside = object_mapping[tile].is_backside;
8254 boolean action_removing = (action == ACTION_DIGGING ||
8255 action == ACTION_SNAPPING ||
8256 action == ACTION_COLLECTING);
8262 case Xacid_splash_e:
8263 case Xacid_splash_w:
8264 return (frame_em > 5 ? EL_EMPTY : element);
8270 else // frame_em == 7
8274 case Xacid_splash_e:
8275 case Xacid_splash_w:
8281 case Ydiamond_stone:
8285 case Xdrip_stretchB:
8301 case Ymagnify_blank:
8304 case Xsand_stonein_1:
8305 case Xsand_stonein_2:
8306 case Xsand_stonein_3:
8307 case Xsand_stonein_4:
8311 return (is_backside || action_removing ? EL_EMPTY : element);
8316 static boolean check_linear_animation_EM(int tile)
8320 case Xsand_stonesand_1:
8321 case Xsand_stonesand_quickout_1:
8322 case Xsand_sandstone_1:
8323 case Xsand_stonein_1:
8324 case Xsand_stoneout_1:
8343 case Xacid_splash_e:
8344 case Xacid_splash_w:
8352 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8353 boolean has_crumbled_graphics,
8354 int crumbled, int sync_frame)
8356 // if element can be crumbled, but certain action graphics are just empty
8357 // space (like instantly snapping sand to empty space in 1 frame), do not
8358 // treat these empty space graphics as crumbled graphics in EMC engine
8359 if (crumbled == IMG_EMPTY_SPACE)
8360 has_crumbled_graphics = FALSE;
8362 if (has_crumbled_graphics)
8364 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8365 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8366 g_crumbled->anim_delay,
8367 g_crumbled->anim_mode,
8368 g_crumbled->anim_start_frame,
8371 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8372 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8374 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8375 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8377 g_em->has_crumbled_graphics = TRUE;
8381 g_em->crumbled_bitmap = NULL;
8382 g_em->crumbled_src_x = 0;
8383 g_em->crumbled_src_y = 0;
8384 g_em->crumbled_border_size = 0;
8385 g_em->crumbled_tile_size = 0;
8387 g_em->has_crumbled_graphics = FALSE;
8392 void ResetGfxAnimation_EM(int x, int y, int tile)
8398 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8399 int tile, int frame_em, int x, int y)
8401 int action = object_mapping[tile].action;
8402 int direction = object_mapping[tile].direction;
8403 int effective_element = get_effective_element_EM(tile, frame_em);
8404 int graphic = (direction == MV_NONE ?
8405 el_act2img(effective_element, action) :
8406 el_act_dir2img(effective_element, action, direction));
8407 struct GraphicInfo *g = &graphic_info[graphic];
8409 boolean action_removing = (action == ACTION_DIGGING ||
8410 action == ACTION_SNAPPING ||
8411 action == ACTION_COLLECTING);
8412 boolean action_moving = (action == ACTION_FALLING ||
8413 action == ACTION_MOVING ||
8414 action == ACTION_PUSHING ||
8415 action == ACTION_EATING ||
8416 action == ACTION_FILLING ||
8417 action == ACTION_EMPTYING);
8418 boolean action_falling = (action == ACTION_FALLING ||
8419 action == ACTION_FILLING ||
8420 action == ACTION_EMPTYING);
8422 // special case: graphic uses "2nd movement tile" and has defined
8423 // 7 frames for movement animation (or less) => use default graphic
8424 // for last (8th) frame which ends the movement animation
8425 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8427 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8428 graphic = (direction == MV_NONE ?
8429 el_act2img(effective_element, action) :
8430 el_act_dir2img(effective_element, action, direction));
8432 g = &graphic_info[graphic];
8435 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8439 else if (action_moving)
8441 boolean is_backside = object_mapping[tile].is_backside;
8445 int direction = object_mapping[tile].direction;
8446 int move_dir = (action_falling ? MV_DOWN : direction);
8451 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8452 if (g->double_movement && frame_em == 0)
8456 if (move_dir == MV_LEFT)
8457 GfxFrame[x - 1][y] = GfxFrame[x][y];
8458 else if (move_dir == MV_RIGHT)
8459 GfxFrame[x + 1][y] = GfxFrame[x][y];
8460 else if (move_dir == MV_UP)
8461 GfxFrame[x][y - 1] = GfxFrame[x][y];
8462 else if (move_dir == MV_DOWN)
8463 GfxFrame[x][y + 1] = GfxFrame[x][y];
8470 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8471 if (tile == Xsand_stonesand_quickout_1 ||
8472 tile == Xsand_stonesand_quickout_2)
8476 if (graphic_info[graphic].anim_global_sync)
8477 sync_frame = FrameCounter;
8478 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8479 sync_frame = GfxFrame[x][y];
8481 sync_frame = 0; // playfield border (pseudo steel)
8483 SetRandomAnimationValue(x, y);
8485 int frame = getAnimationFrame(g->anim_frames,
8488 g->anim_start_frame,
8491 g_em->unique_identifier =
8492 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8495 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8496 int tile, int frame_em, int x, int y)
8498 int action = object_mapping[tile].action;
8499 int direction = object_mapping[tile].direction;
8500 boolean is_backside = object_mapping[tile].is_backside;
8501 int effective_element = get_effective_element_EM(tile, frame_em);
8502 int effective_action = action;
8503 int graphic = (direction == MV_NONE ?
8504 el_act2img(effective_element, effective_action) :
8505 el_act_dir2img(effective_element, effective_action,
8507 int crumbled = (direction == MV_NONE ?
8508 el_act2crm(effective_element, effective_action) :
8509 el_act_dir2crm(effective_element, effective_action,
8511 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8512 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8513 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8514 struct GraphicInfo *g = &graphic_info[graphic];
8517 // special case: graphic uses "2nd movement tile" and has defined
8518 // 7 frames for movement animation (or less) => use default graphic
8519 // for last (8th) frame which ends the movement animation
8520 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8522 effective_action = ACTION_DEFAULT;
8523 graphic = (direction == MV_NONE ?
8524 el_act2img(effective_element, effective_action) :
8525 el_act_dir2img(effective_element, effective_action,
8527 crumbled = (direction == MV_NONE ?
8528 el_act2crm(effective_element, effective_action) :
8529 el_act_dir2crm(effective_element, effective_action,
8532 g = &graphic_info[graphic];
8535 if (graphic_info[graphic].anim_global_sync)
8536 sync_frame = FrameCounter;
8537 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8538 sync_frame = GfxFrame[x][y];
8540 sync_frame = 0; // playfield border (pseudo steel)
8542 SetRandomAnimationValue(x, y);
8544 int frame = getAnimationFrame(g->anim_frames,
8547 g->anim_start_frame,
8550 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8551 g->double_movement && is_backside);
8553 // (updating the "crumbled" graphic definitions is probably not really needed,
8554 // as animations for crumbled graphics can't be longer than one EMC cycle)
8555 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8559 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8560 int player_nr, int anim, int frame_em)
8562 int element = player_mapping[player_nr][anim].element_rnd;
8563 int action = player_mapping[player_nr][anim].action;
8564 int direction = player_mapping[player_nr][anim].direction;
8565 int graphic = (direction == MV_NONE ?
8566 el_act2img(element, action) :
8567 el_act_dir2img(element, action, direction));
8568 struct GraphicInfo *g = &graphic_info[graphic];
8571 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8573 stored_player[player_nr].StepFrame = frame_em;
8575 sync_frame = stored_player[player_nr].Frame;
8577 int frame = getAnimationFrame(g->anim_frames,
8580 g->anim_start_frame,
8583 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8584 &g_em->src_x, &g_em->src_y, FALSE);
8587 void InitGraphicInfo_EM(void)
8592 int num_em_gfx_errors = 0;
8594 if (graphic_info_em_object[0][0].bitmap == NULL)
8596 // EM graphics not yet initialized in em_open_all()
8601 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8604 // always start with reliable default values
8605 for (i = 0; i < TILE_MAX; i++)
8607 object_mapping[i].element_rnd = EL_UNKNOWN;
8608 object_mapping[i].is_backside = FALSE;
8609 object_mapping[i].action = ACTION_DEFAULT;
8610 object_mapping[i].direction = MV_NONE;
8613 // always start with reliable default values
8614 for (p = 0; p < MAX_PLAYERS; p++)
8616 for (i = 0; i < PLY_MAX; i++)
8618 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8619 player_mapping[p][i].action = ACTION_DEFAULT;
8620 player_mapping[p][i].direction = MV_NONE;
8624 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8626 int e = em_object_mapping_list[i].element_em;
8628 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8629 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8631 if (em_object_mapping_list[i].action != -1)
8632 object_mapping[e].action = em_object_mapping_list[i].action;
8634 if (em_object_mapping_list[i].direction != -1)
8635 object_mapping[e].direction =
8636 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8639 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8641 int a = em_player_mapping_list[i].action_em;
8642 int p = em_player_mapping_list[i].player_nr;
8644 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8646 if (em_player_mapping_list[i].action != -1)
8647 player_mapping[p][a].action = em_player_mapping_list[i].action;
8649 if (em_player_mapping_list[i].direction != -1)
8650 player_mapping[p][a].direction =
8651 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8654 for (i = 0; i < TILE_MAX; i++)
8656 int element = object_mapping[i].element_rnd;
8657 int action = object_mapping[i].action;
8658 int direction = object_mapping[i].direction;
8659 boolean is_backside = object_mapping[i].is_backside;
8660 boolean action_exploding = ((action == ACTION_EXPLODING ||
8661 action == ACTION_SMASHED_BY_ROCK ||
8662 action == ACTION_SMASHED_BY_SPRING) &&
8663 element != EL_DIAMOND);
8664 boolean action_active = (action == ACTION_ACTIVE);
8665 boolean action_other = (action == ACTION_OTHER);
8667 for (j = 0; j < 8; j++)
8669 int effective_element = get_effective_element_EM(i, j);
8670 int effective_action = (j < 7 ? action :
8671 i == Xdrip_stretch ? action :
8672 i == Xdrip_stretchB ? action :
8673 i == Ydrip_1_s ? action :
8674 i == Ydrip_1_sB ? action :
8675 i == Yball_1 ? action :
8676 i == Xball_2 ? action :
8677 i == Yball_2 ? action :
8678 i == Yball_blank ? action :
8679 i == Ykey_1_blank ? action :
8680 i == Ykey_2_blank ? action :
8681 i == Ykey_3_blank ? action :
8682 i == Ykey_4_blank ? action :
8683 i == Ykey_5_blank ? action :
8684 i == Ykey_6_blank ? action :
8685 i == Ykey_7_blank ? action :
8686 i == Ykey_8_blank ? action :
8687 i == Ylenses_blank ? action :
8688 i == Ymagnify_blank ? action :
8689 i == Ygrass_blank ? action :
8690 i == Ydirt_blank ? action :
8691 i == Xsand_stonein_1 ? action :
8692 i == Xsand_stonein_2 ? action :
8693 i == Xsand_stonein_3 ? action :
8694 i == Xsand_stonein_4 ? action :
8695 i == Xsand_stoneout_1 ? action :
8696 i == Xsand_stoneout_2 ? action :
8697 i == Xboom_android ? ACTION_EXPLODING :
8698 action_exploding ? ACTION_EXPLODING :
8699 action_active ? action :
8700 action_other ? action :
8702 int graphic = (el_act_dir2img(effective_element, effective_action,
8704 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8706 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8707 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8708 boolean has_action_graphics = (graphic != base_graphic);
8709 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8710 struct GraphicInfo *g = &graphic_info[graphic];
8711 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8714 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8715 boolean special_animation = (action != ACTION_DEFAULT &&
8716 g->anim_frames == 3 &&
8717 g->anim_delay == 2 &&
8718 g->anim_mode & ANIM_LINEAR);
8719 int sync_frame = (i == Xdrip_stretch ? 7 :
8720 i == Xdrip_stretchB ? 7 :
8721 i == Ydrip_2_s ? j + 8 :
8722 i == Ydrip_2_sB ? j + 8 :
8731 i == Xfake_acid_1 ? 0 :
8732 i == Xfake_acid_2 ? 10 :
8733 i == Xfake_acid_3 ? 20 :
8734 i == Xfake_acid_4 ? 30 :
8735 i == Xfake_acid_5 ? 40 :
8736 i == Xfake_acid_6 ? 50 :
8737 i == Xfake_acid_7 ? 60 :
8738 i == Xfake_acid_8 ? 70 :
8740 i == Yball_2 ? j + 8 :
8741 i == Yball_blank ? j + 1 :
8742 i == Ykey_1_blank ? j + 1 :
8743 i == Ykey_2_blank ? j + 1 :
8744 i == Ykey_3_blank ? j + 1 :
8745 i == Ykey_4_blank ? j + 1 :
8746 i == Ykey_5_blank ? j + 1 :
8747 i == Ykey_6_blank ? j + 1 :
8748 i == Ykey_7_blank ? j + 1 :
8749 i == Ykey_8_blank ? j + 1 :
8750 i == Ylenses_blank ? j + 1 :
8751 i == Ymagnify_blank ? j + 1 :
8752 i == Ygrass_blank ? j + 1 :
8753 i == Ydirt_blank ? j + 1 :
8754 i == Xamoeba_1 ? 0 :
8755 i == Xamoeba_2 ? 1 :
8756 i == Xamoeba_3 ? 2 :
8757 i == Xamoeba_4 ? 3 :
8758 i == Xamoeba_5 ? 0 :
8759 i == Xamoeba_6 ? 1 :
8760 i == Xamoeba_7 ? 2 :
8761 i == Xamoeba_8 ? 3 :
8762 i == Xexit_2 ? j + 8 :
8763 i == Xexit_3 ? j + 16 :
8764 i == Xdynamite_1 ? 0 :
8765 i == Xdynamite_2 ? 8 :
8766 i == Xdynamite_3 ? 16 :
8767 i == Xdynamite_4 ? 24 :
8768 i == Xsand_stonein_1 ? j + 1 :
8769 i == Xsand_stonein_2 ? j + 9 :
8770 i == Xsand_stonein_3 ? j + 17 :
8771 i == Xsand_stonein_4 ? j + 25 :
8772 i == Xsand_stoneout_1 && j == 0 ? 0 :
8773 i == Xsand_stoneout_1 && j == 1 ? 0 :
8774 i == Xsand_stoneout_1 && j == 2 ? 1 :
8775 i == Xsand_stoneout_1 && j == 3 ? 2 :
8776 i == Xsand_stoneout_1 && j == 4 ? 2 :
8777 i == Xsand_stoneout_1 && j == 5 ? 3 :
8778 i == Xsand_stoneout_1 && j == 6 ? 4 :
8779 i == Xsand_stoneout_1 && j == 7 ? 4 :
8780 i == Xsand_stoneout_2 && j == 0 ? 5 :
8781 i == Xsand_stoneout_2 && j == 1 ? 6 :
8782 i == Xsand_stoneout_2 && j == 2 ? 7 :
8783 i == Xsand_stoneout_2 && j == 3 ? 8 :
8784 i == Xsand_stoneout_2 && j == 4 ? 9 :
8785 i == Xsand_stoneout_2 && j == 5 ? 11 :
8786 i == Xsand_stoneout_2 && j == 6 ? 13 :
8787 i == Xsand_stoneout_2 && j == 7 ? 15 :
8788 i == Xboom_bug && j == 1 ? 2 :
8789 i == Xboom_bug && j == 2 ? 2 :
8790 i == Xboom_bug && j == 3 ? 4 :
8791 i == Xboom_bug && j == 4 ? 4 :
8792 i == Xboom_bug && j == 5 ? 2 :
8793 i == Xboom_bug && j == 6 ? 2 :
8794 i == Xboom_bug && j == 7 ? 0 :
8795 i == Xboom_bomb && j == 1 ? 2 :
8796 i == Xboom_bomb && j == 2 ? 2 :
8797 i == Xboom_bomb && j == 3 ? 4 :
8798 i == Xboom_bomb && j == 4 ? 4 :
8799 i == Xboom_bomb && j == 5 ? 2 :
8800 i == Xboom_bomb && j == 6 ? 2 :
8801 i == Xboom_bomb && j == 7 ? 0 :
8802 i == Xboom_android && j == 7 ? 6 :
8803 i == Xboom_1 && j == 1 ? 2 :
8804 i == Xboom_1 && j == 2 ? 2 :
8805 i == Xboom_1 && j == 3 ? 4 :
8806 i == Xboom_1 && j == 4 ? 4 :
8807 i == Xboom_1 && j == 5 ? 6 :
8808 i == Xboom_1 && j == 6 ? 6 :
8809 i == Xboom_1 && j == 7 ? 8 :
8810 i == Xboom_2 && j == 0 ? 8 :
8811 i == Xboom_2 && j == 1 ? 8 :
8812 i == Xboom_2 && j == 2 ? 10 :
8813 i == Xboom_2 && j == 3 ? 10 :
8814 i == Xboom_2 && j == 4 ? 10 :
8815 i == Xboom_2 && j == 5 ? 12 :
8816 i == Xboom_2 && j == 6 ? 12 :
8817 i == Xboom_2 && j == 7 ? 12 :
8818 special_animation && j == 4 ? 3 :
8819 effective_action != action ? 0 :
8823 Bitmap *debug_bitmap = g_em->bitmap;
8824 int debug_src_x = g_em->src_x;
8825 int debug_src_y = g_em->src_y;
8828 int frame = getAnimationFrame(g->anim_frames,
8831 g->anim_start_frame,
8834 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8835 g->double_movement && is_backside);
8837 g_em->bitmap = src_bitmap;
8838 g_em->src_x = src_x;
8839 g_em->src_y = src_y;
8840 g_em->src_offset_x = 0;
8841 g_em->src_offset_y = 0;
8842 g_em->dst_offset_x = 0;
8843 g_em->dst_offset_y = 0;
8844 g_em->width = TILEX;
8845 g_em->height = TILEY;
8847 g_em->preserve_background = FALSE;
8849 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8852 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8853 effective_action == ACTION_MOVING ||
8854 effective_action == ACTION_PUSHING ||
8855 effective_action == ACTION_EATING)) ||
8856 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8857 effective_action == ACTION_EMPTYING)))
8860 (effective_action == ACTION_FALLING ||
8861 effective_action == ACTION_FILLING ||
8862 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8863 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8864 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8865 int num_steps = (i == Ydrip_1_s ? 16 :
8866 i == Ydrip_1_sB ? 16 :
8867 i == Ydrip_2_s ? 16 :
8868 i == Ydrip_2_sB ? 16 :
8869 i == Xsand_stonein_1 ? 32 :
8870 i == Xsand_stonein_2 ? 32 :
8871 i == Xsand_stonein_3 ? 32 :
8872 i == Xsand_stonein_4 ? 32 :
8873 i == Xsand_stoneout_1 ? 16 :
8874 i == Xsand_stoneout_2 ? 16 : 8);
8875 int cx = ABS(dx) * (TILEX / num_steps);
8876 int cy = ABS(dy) * (TILEY / num_steps);
8877 int step_frame = (i == Ydrip_2_s ? j + 8 :
8878 i == Ydrip_2_sB ? j + 8 :
8879 i == Xsand_stonein_2 ? j + 8 :
8880 i == Xsand_stonein_3 ? j + 16 :
8881 i == Xsand_stonein_4 ? j + 24 :
8882 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8883 int step = (is_backside ? step_frame : num_steps - step_frame);
8885 if (is_backside) // tile where movement starts
8887 if (dx < 0 || dy < 0)
8889 g_em->src_offset_x = cx * step;
8890 g_em->src_offset_y = cy * step;
8894 g_em->dst_offset_x = cx * step;
8895 g_em->dst_offset_y = cy * step;
8898 else // tile where movement ends
8900 if (dx < 0 || dy < 0)
8902 g_em->dst_offset_x = cx * step;
8903 g_em->dst_offset_y = cy * step;
8907 g_em->src_offset_x = cx * step;
8908 g_em->src_offset_y = cy * step;
8912 g_em->width = TILEX - cx * step;
8913 g_em->height = TILEY - cy * step;
8916 // create unique graphic identifier to decide if tile must be redrawn
8917 /* bit 31 - 16 (16 bit): EM style graphic
8918 bit 15 - 12 ( 4 bit): EM style frame
8919 bit 11 - 6 ( 6 bit): graphic width
8920 bit 5 - 0 ( 6 bit): graphic height */
8921 g_em->unique_identifier =
8922 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8926 // skip check for EMC elements not contained in original EMC artwork
8927 if (element == EL_EMC_FAKE_ACID)
8930 if (g_em->bitmap != debug_bitmap ||
8931 g_em->src_x != debug_src_x ||
8932 g_em->src_y != debug_src_y ||
8933 g_em->src_offset_x != 0 ||
8934 g_em->src_offset_y != 0 ||
8935 g_em->dst_offset_x != 0 ||
8936 g_em->dst_offset_y != 0 ||
8937 g_em->width != TILEX ||
8938 g_em->height != TILEY)
8940 static int last_i = -1;
8948 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8949 i, element, element_info[element].token_name,
8950 element_action_info[effective_action].suffix, direction);
8952 if (element != effective_element)
8953 printf(" [%d ('%s')]",
8955 element_info[effective_element].token_name);
8959 if (g_em->bitmap != debug_bitmap)
8960 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8961 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8963 if (g_em->src_x != debug_src_x ||
8964 g_em->src_y != debug_src_y)
8965 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8966 j, (is_backside ? 'B' : 'F'),
8967 g_em->src_x, g_em->src_y,
8968 g_em->src_x / 32, g_em->src_y / 32,
8969 debug_src_x, debug_src_y,
8970 debug_src_x / 32, debug_src_y / 32);
8972 if (g_em->src_offset_x != 0 ||
8973 g_em->src_offset_y != 0 ||
8974 g_em->dst_offset_x != 0 ||
8975 g_em->dst_offset_y != 0)
8976 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8978 g_em->src_offset_x, g_em->src_offset_y,
8979 g_em->dst_offset_x, g_em->dst_offset_y);
8981 if (g_em->width != TILEX ||
8982 g_em->height != TILEY)
8983 printf(" %d (%d): size %d,%d should be %d,%d\n",
8985 g_em->width, g_em->height, TILEX, TILEY);
8987 num_em_gfx_errors++;
8994 for (i = 0; i < TILE_MAX; i++)
8996 for (j = 0; j < 8; j++)
8998 int element = object_mapping[i].element_rnd;
8999 int action = object_mapping[i].action;
9000 int direction = object_mapping[i].direction;
9001 boolean is_backside = object_mapping[i].is_backside;
9002 int graphic_action = el_act_dir2img(element, action, direction);
9003 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
9005 if ((action == ACTION_SMASHED_BY_ROCK ||
9006 action == ACTION_SMASHED_BY_SPRING ||
9007 action == ACTION_EATING) &&
9008 graphic_action == graphic_default)
9010 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
9011 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
9012 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
9013 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
9016 // no separate animation for "smashed by rock" -- use rock instead
9017 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
9018 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
9020 g_em->bitmap = g_xx->bitmap;
9021 g_em->src_x = g_xx->src_x;
9022 g_em->src_y = g_xx->src_y;
9023 g_em->src_offset_x = g_xx->src_offset_x;
9024 g_em->src_offset_y = g_xx->src_offset_y;
9025 g_em->dst_offset_x = g_xx->dst_offset_x;
9026 g_em->dst_offset_y = g_xx->dst_offset_y;
9027 g_em->width = g_xx->width;
9028 g_em->height = g_xx->height;
9029 g_em->unique_identifier = g_xx->unique_identifier;
9032 g_em->preserve_background = TRUE;
9037 for (p = 0; p < MAX_PLAYERS; p++)
9039 for (i = 0; i < PLY_MAX; i++)
9041 int element = player_mapping[p][i].element_rnd;
9042 int action = player_mapping[p][i].action;
9043 int direction = player_mapping[p][i].direction;
9045 for (j = 0; j < 8; j++)
9047 int effective_element = element;
9048 int effective_action = action;
9049 int graphic = (direction == MV_NONE ?
9050 el_act2img(effective_element, effective_action) :
9051 el_act_dir2img(effective_element, effective_action,
9053 struct GraphicInfo *g = &graphic_info[graphic];
9054 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
9060 Bitmap *debug_bitmap = g_em->bitmap;
9061 int debug_src_x = g_em->src_x;
9062 int debug_src_y = g_em->src_y;
9065 int frame = getAnimationFrame(g->anim_frames,
9068 g->anim_start_frame,
9071 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9073 g_em->bitmap = src_bitmap;
9074 g_em->src_x = src_x;
9075 g_em->src_y = src_y;
9076 g_em->src_offset_x = 0;
9077 g_em->src_offset_y = 0;
9078 g_em->dst_offset_x = 0;
9079 g_em->dst_offset_y = 0;
9080 g_em->width = TILEX;
9081 g_em->height = TILEY;
9085 // skip check for EMC elements not contained in original EMC artwork
9086 if (element == EL_PLAYER_3 ||
9087 element == EL_PLAYER_4)
9090 if (g_em->bitmap != debug_bitmap ||
9091 g_em->src_x != debug_src_x ||
9092 g_em->src_y != debug_src_y)
9094 static int last_i = -1;
9102 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9103 p, i, element, element_info[element].token_name,
9104 element_action_info[effective_action].suffix, direction);
9106 if (element != effective_element)
9107 printf(" [%d ('%s')]",
9109 element_info[effective_element].token_name);
9113 if (g_em->bitmap != debug_bitmap)
9114 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9115 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9117 if (g_em->src_x != debug_src_x ||
9118 g_em->src_y != debug_src_y)
9119 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9121 g_em->src_x, g_em->src_y,
9122 g_em->src_x / 32, g_em->src_y / 32,
9123 debug_src_x, debug_src_y,
9124 debug_src_x / 32, debug_src_y / 32);
9126 num_em_gfx_errors++;
9136 printf("::: [%d errors found]\n", num_em_gfx_errors);
9142 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9143 boolean any_player_moving,
9144 boolean any_player_snapping,
9145 boolean any_player_dropping)
9147 if (frame == 0 && !any_player_dropping)
9149 if (!local_player->was_waiting)
9151 if (!CheckSaveEngineSnapshotToList())
9154 local_player->was_waiting = TRUE;
9157 else if (any_player_moving || any_player_snapping || any_player_dropping)
9159 local_player->was_waiting = FALSE;
9163 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9164 boolean murphy_is_dropping)
9166 if (murphy_is_waiting)
9168 if (!local_player->was_waiting)
9170 if (!CheckSaveEngineSnapshotToList())
9173 local_player->was_waiting = TRUE;
9178 local_player->was_waiting = FALSE;
9182 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9183 boolean button_released)
9185 if (button_released)
9187 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9188 CheckSaveEngineSnapshotToList();
9190 else if (element_clicked)
9192 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9193 CheckSaveEngineSnapshotToList();
9195 game.snapshot.changed_action = TRUE;
9199 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9200 boolean any_player_moving,
9201 boolean any_player_snapping,
9202 boolean any_player_dropping)
9204 if (tape.single_step && tape.recording && !tape.pausing)
9205 if (frame == 0 && !any_player_dropping)
9206 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9208 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9209 any_player_snapping, any_player_dropping);
9212 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9213 boolean murphy_is_dropping)
9215 boolean murphy_starts_dropping = FALSE;
9218 for (i = 0; i < MAX_PLAYERS; i++)
9219 if (stored_player[i].force_dropping)
9220 murphy_starts_dropping = TRUE;
9222 if (tape.single_step && tape.recording && !tape.pausing)
9223 if (murphy_is_waiting && !murphy_starts_dropping)
9224 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9226 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9229 void CheckSingleStepMode_MM(boolean element_clicked,
9230 boolean button_released)
9232 if (tape.single_step && tape.recording && !tape.pausing)
9233 if (button_released)
9234 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9236 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9239 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9240 int graphic, int sync_frame, int x, int y)
9242 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9244 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9247 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9249 return (IS_NEXT_FRAME(sync_frame, graphic));
9252 int getGraphicInfo_Delay(int graphic)
9254 return graphic_info[graphic].anim_delay;
9257 void PlayMenuSoundExt(int sound)
9259 if (sound == SND_UNDEFINED)
9262 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9263 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9266 if (IS_LOOP_SOUND(sound))
9267 PlaySoundLoop(sound);
9272 void PlayMenuSound(void)
9274 PlayMenuSoundExt(menu.sound[game_status]);
9277 void PlayMenuSoundStereo(int sound, int stereo_position)
9279 if (sound == SND_UNDEFINED)
9282 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9283 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9286 if (IS_LOOP_SOUND(sound))
9287 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9289 PlaySoundStereo(sound, stereo_position);
9292 void PlayMenuSoundIfLoopExt(int sound)
9294 if (sound == SND_UNDEFINED)
9297 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9298 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9301 if (IS_LOOP_SOUND(sound))
9302 PlaySoundLoop(sound);
9305 void PlayMenuSoundIfLoop(void)
9307 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9310 void PlayMenuMusicExt(int music)
9312 if (music == MUS_UNDEFINED)
9315 if (!setup.sound_music)
9318 if (IS_LOOP_MUSIC(music))
9319 PlayMusicLoop(music);
9324 void PlayMenuMusic(void)
9326 char *curr_music = getCurrentlyPlayingMusicFilename();
9327 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9329 if (!strEqual(curr_music, next_music))
9330 PlayMenuMusicExt(menu.music[game_status]);
9333 void PlayMenuSoundsAndMusic(void)
9339 static void FadeMenuSounds(void)
9344 static void FadeMenuMusic(void)
9346 char *curr_music = getCurrentlyPlayingMusicFilename();
9347 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9349 if (!strEqual(curr_music, next_music))
9353 void FadeMenuSoundsAndMusic(void)
9359 void PlaySoundActivating(void)
9362 PlaySound(SND_MENU_ITEM_ACTIVATING);
9366 void PlaySoundSelecting(void)
9369 PlaySound(SND_MENU_ITEM_SELECTING);
9373 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9375 boolean change_fullscreen = (setup.fullscreen !=
9376 video.fullscreen_enabled);
9377 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9378 setup.window_scaling_percent !=
9379 video.window_scaling_percent);
9381 if (change_window_scaling_percent && video.fullscreen_enabled)
9384 if (!change_window_scaling_percent && !video.fullscreen_available)
9387 if (change_window_scaling_percent)
9389 SDLSetWindowScaling(setup.window_scaling_percent);
9393 else if (change_fullscreen)
9395 SDLSetWindowFullscreen(setup.fullscreen);
9397 // set setup value according to successfully changed fullscreen mode
9398 setup.fullscreen = video.fullscreen_enabled;
9403 if (change_fullscreen ||
9404 change_window_scaling_percent)
9406 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9408 // save backbuffer content which gets lost when toggling fullscreen mode
9409 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9411 if (change_window_scaling_percent)
9413 // keep window mode, but change window scaling
9414 video.fullscreen_enabled = TRUE; // force new window scaling
9417 // toggle fullscreen
9418 ChangeVideoModeIfNeeded(setup.fullscreen);
9420 // set setup value according to successfully changed fullscreen mode
9421 setup.fullscreen = video.fullscreen_enabled;
9423 // restore backbuffer content from temporary backbuffer backup bitmap
9424 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9426 FreeBitmap(tmp_backbuffer);
9428 // update visible window/screen
9429 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9433 static void JoinRectangles(int *x, int *y, int *width, int *height,
9434 int x2, int y2, int width2, int height2)
9436 // do not join with "off-screen" rectangle
9437 if (x2 == -1 || y2 == -1)
9442 *width = MAX(*width, width2);
9443 *height = MAX(*height, height2);
9446 void SetAnimStatus(int anim_status_new)
9448 if (anim_status_new == GAME_MODE_MAIN)
9449 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9450 else if (anim_status_new == GAME_MODE_SCORES)
9451 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9453 global.anim_status_next = anim_status_new;
9455 // directly set screen modes that are entered without fading
9456 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9457 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9458 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9459 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9460 global.anim_status = global.anim_status_next;
9463 void SetGameStatus(int game_status_new)
9465 if (game_status_new != game_status)
9466 game_status_last_screen = game_status;
9468 game_status = game_status_new;
9470 SetAnimStatus(game_status_new);
9473 void SetFontStatus(int game_status_new)
9475 static int last_game_status = -1;
9477 if (game_status_new != -1)
9479 // set game status for font use after storing last game status
9480 last_game_status = game_status;
9481 game_status = game_status_new;
9485 // reset game status after font use from last stored game status
9486 game_status = last_game_status;
9490 void ResetFontStatus(void)
9495 void SetLevelSetInfo(char *identifier, int level_nr)
9497 setString(&levelset.identifier, identifier);
9499 levelset.level_nr = level_nr;
9502 boolean CheckIfAllViewportsHaveChanged(void)
9504 // if game status has not changed, viewports have not changed either
9505 if (game_status == game_status_last)
9508 // check if all viewports have changed with current game status
9510 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9511 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9512 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9513 int new_real_sx = vp_playfield->x;
9514 int new_real_sy = vp_playfield->y;
9515 int new_full_sxsize = vp_playfield->width;
9516 int new_full_sysize = vp_playfield->height;
9517 int new_dx = vp_door_1->x;
9518 int new_dy = vp_door_1->y;
9519 int new_dxsize = vp_door_1->width;
9520 int new_dysize = vp_door_1->height;
9521 int new_vx = vp_door_2->x;
9522 int new_vy = vp_door_2->y;
9523 int new_vxsize = vp_door_2->width;
9524 int new_vysize = vp_door_2->height;
9526 boolean playfield_viewport_has_changed =
9527 (new_real_sx != REAL_SX ||
9528 new_real_sy != REAL_SY ||
9529 new_full_sxsize != FULL_SXSIZE ||
9530 new_full_sysize != FULL_SYSIZE);
9532 boolean door_1_viewport_has_changed =
9535 new_dxsize != DXSIZE ||
9536 new_dysize != DYSIZE);
9538 boolean door_2_viewport_has_changed =
9541 new_vxsize != VXSIZE ||
9542 new_vysize != VYSIZE ||
9543 game_status_last == GAME_MODE_EDITOR);
9545 return (playfield_viewport_has_changed &&
9546 door_1_viewport_has_changed &&
9547 door_2_viewport_has_changed);
9550 boolean CheckFadeAll(void)
9552 return (CheckIfGlobalBorderHasChanged() ||
9553 CheckIfAllViewportsHaveChanged());
9556 void ChangeViewportPropertiesIfNeeded(void)
9558 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9559 FALSE : setup.small_game_graphics);
9560 int gfx_game_mode = game_status;
9561 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9563 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9564 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9565 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9566 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9567 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9568 int new_win_xsize = vp_window->width;
9569 int new_win_ysize = vp_window->height;
9570 int border_left = vp_playfield->border_left;
9571 int border_right = vp_playfield->border_right;
9572 int border_top = vp_playfield->border_top;
9573 int border_bottom = vp_playfield->border_bottom;
9574 int new_sx = vp_playfield->x + border_left;
9575 int new_sy = vp_playfield->y + border_top;
9576 int new_sxsize = vp_playfield->width - border_left - border_right;
9577 int new_sysize = vp_playfield->height - border_top - border_bottom;
9578 int new_real_sx = vp_playfield->x;
9579 int new_real_sy = vp_playfield->y;
9580 int new_full_sxsize = vp_playfield->width;
9581 int new_full_sysize = vp_playfield->height;
9582 int new_dx = vp_door_1->x;
9583 int new_dy = vp_door_1->y;
9584 int new_dxsize = vp_door_1->width;
9585 int new_dysize = vp_door_1->height;
9586 int new_vx = vp_door_2->x;
9587 int new_vy = vp_door_2->y;
9588 int new_vxsize = vp_door_2->width;
9589 int new_vysize = vp_door_2->height;
9590 int new_ex = vp_door_3->x;
9591 int new_ey = vp_door_3->y;
9592 int new_exsize = vp_door_3->width;
9593 int new_eysize = vp_door_3->height;
9594 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9595 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9596 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9597 int new_scr_fieldx = new_sxsize / tilesize;
9598 int new_scr_fieldy = new_sysize / tilesize;
9599 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9600 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9601 boolean init_gfx_buffers = FALSE;
9602 boolean init_video_buffer = FALSE;
9603 boolean init_gadgets_and_anims = FALSE;
9604 boolean init_em_graphics = FALSE;
9606 if (new_win_xsize != WIN_XSIZE ||
9607 new_win_ysize != WIN_YSIZE)
9609 WIN_XSIZE = new_win_xsize;
9610 WIN_YSIZE = new_win_ysize;
9612 init_video_buffer = TRUE;
9613 init_gfx_buffers = TRUE;
9614 init_gadgets_and_anims = TRUE;
9616 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9619 if (new_scr_fieldx != SCR_FIELDX ||
9620 new_scr_fieldy != SCR_FIELDY)
9622 // this always toggles between MAIN and GAME when using small tile size
9624 SCR_FIELDX = new_scr_fieldx;
9625 SCR_FIELDY = new_scr_fieldy;
9627 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9638 new_sxsize != SXSIZE ||
9639 new_sysize != SYSIZE ||
9640 new_dxsize != DXSIZE ||
9641 new_dysize != DYSIZE ||
9642 new_vxsize != VXSIZE ||
9643 new_vysize != VYSIZE ||
9644 new_exsize != EXSIZE ||
9645 new_eysize != EYSIZE ||
9646 new_real_sx != REAL_SX ||
9647 new_real_sy != REAL_SY ||
9648 new_full_sxsize != FULL_SXSIZE ||
9649 new_full_sysize != FULL_SYSIZE ||
9650 new_tilesize_var != TILESIZE_VAR
9653 // ------------------------------------------------------------------------
9654 // determine next fading area for changed viewport definitions
9655 // ------------------------------------------------------------------------
9657 // start with current playfield area (default fading area)
9660 FADE_SXSIZE = FULL_SXSIZE;
9661 FADE_SYSIZE = FULL_SYSIZE;
9663 // add new playfield area if position or size has changed
9664 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9665 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9667 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9668 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9671 // add current and new door 1 area if position or size has changed
9672 if (new_dx != DX || new_dy != DY ||
9673 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9675 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9676 DX, DY, DXSIZE, DYSIZE);
9677 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9678 new_dx, new_dy, new_dxsize, new_dysize);
9681 // add current and new door 2 area if position or size has changed
9682 if (new_vx != VX || new_vy != VY ||
9683 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9685 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9686 VX, VY, VXSIZE, VYSIZE);
9687 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9688 new_vx, new_vy, new_vxsize, new_vysize);
9691 // ------------------------------------------------------------------------
9692 // handle changed tile size
9693 // ------------------------------------------------------------------------
9695 if (new_tilesize_var != TILESIZE_VAR)
9697 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9699 // changing tile size invalidates scroll values of engine snapshots
9700 FreeEngineSnapshotSingle();
9702 // changing tile size requires update of graphic mapping for EM engine
9703 init_em_graphics = TRUE;
9714 SXSIZE = new_sxsize;
9715 SYSIZE = new_sysize;
9716 DXSIZE = new_dxsize;
9717 DYSIZE = new_dysize;
9718 VXSIZE = new_vxsize;
9719 VYSIZE = new_vysize;
9720 EXSIZE = new_exsize;
9721 EYSIZE = new_eysize;
9722 REAL_SX = new_real_sx;
9723 REAL_SY = new_real_sy;
9724 FULL_SXSIZE = new_full_sxsize;
9725 FULL_SYSIZE = new_full_sysize;
9726 TILESIZE_VAR = new_tilesize_var;
9728 init_gfx_buffers = TRUE;
9729 init_gadgets_and_anims = TRUE;
9731 // printf("::: viewports: init_gfx_buffers\n");
9732 // printf("::: viewports: init_gadgets_and_anims\n");
9735 if (init_gfx_buffers)
9737 // printf("::: init_gfx_buffers\n");
9739 SCR_FIELDX = new_scr_fieldx_buffers;
9740 SCR_FIELDY = new_scr_fieldy_buffers;
9744 SCR_FIELDX = new_scr_fieldx;
9745 SCR_FIELDY = new_scr_fieldy;
9747 SetDrawDeactivationMask(REDRAW_NONE);
9748 SetDrawBackgroundMask(REDRAW_FIELD);
9751 if (init_video_buffer)
9753 // printf("::: init_video_buffer\n");
9755 FreeAllImageTextures(); // needs old renderer to free the textures
9757 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9758 InitImageTextures();
9761 if (init_gadgets_and_anims)
9763 // printf("::: init_gadgets_and_anims\n");
9766 InitGlobalAnimations();
9769 if (init_em_graphics)
9771 InitGraphicInfo_EM();