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,
5748 Xacid_splash_e, FALSE, FALSE,
5749 EL_ACID_SPLASH_RIGHT, -1, -1
5752 Xacid_splash_w, FALSE, FALSE,
5753 EL_ACID_SPLASH_LEFT, -1, -1
5756 Xpush_stone_e, FALSE, FALSE,
5757 EL_ROCK, -1, MV_BIT_RIGHT
5760 Xpush_stone_w, FALSE, FALSE,
5761 EL_ROCK, -1, MV_BIT_LEFT
5764 Xpush_nut_e, FALSE, FALSE,
5765 EL_NUT, -1, MV_BIT_RIGHT
5768 Xpush_nut_w, FALSE, FALSE,
5769 EL_NUT, -1, MV_BIT_LEFT
5772 Xpush_spring_e, FALSE, FALSE,
5773 EL_SPRING, -1, MV_BIT_RIGHT
5776 Xpush_spring_w, FALSE, FALSE,
5777 EL_SPRING, -1, MV_BIT_LEFT
5780 Xpush_emerald_e, FALSE, FALSE,
5781 EL_EMERALD, -1, MV_BIT_RIGHT
5784 Xpush_emerald_w, FALSE, FALSE,
5785 EL_EMERALD, -1, MV_BIT_LEFT
5788 Xpush_diamond_e, FALSE, FALSE,
5789 EL_DIAMOND, -1, MV_BIT_RIGHT
5792 Xpush_diamond_w, FALSE, FALSE,
5793 EL_DIAMOND, -1, MV_BIT_LEFT
5796 Xpush_bomb_e, FALSE, FALSE,
5797 EL_BOMB, -1, MV_BIT_RIGHT
5800 Xpush_bomb_w, FALSE, FALSE,
5801 EL_BOMB, -1, MV_BIT_LEFT
5804 Xstone, TRUE, FALSE,
5808 Xstone_pause, FALSE, FALSE,
5812 Xstone_fall, FALSE, FALSE,
5816 Ystone_s, FALSE, FALSE,
5817 EL_ROCK, ACTION_FALLING, -1
5820 Ystone_sB, FALSE, TRUE,
5821 EL_ROCK, ACTION_FALLING, -1
5824 Ystone_e, FALSE, FALSE,
5825 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5828 Ystone_eB, FALSE, TRUE,
5829 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5832 Ystone_w, FALSE, FALSE,
5833 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5836 Ystone_wB, FALSE, TRUE,
5837 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5844 Xnut_pause, FALSE, FALSE,
5848 Xnut_fall, FALSE, FALSE,
5852 Ynut_s, FALSE, FALSE,
5853 EL_NUT, ACTION_FALLING, -1
5856 Ynut_sB, FALSE, TRUE,
5857 EL_NUT, ACTION_FALLING, -1
5860 Ynut_e, FALSE, FALSE,
5861 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5864 Ynut_eB, FALSE, TRUE,
5865 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5868 Ynut_w, FALSE, FALSE,
5869 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5872 Ynut_wB, FALSE, TRUE,
5873 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5876 Xbug_1_n, TRUE, FALSE,
5880 Xbug_1_e, TRUE, FALSE,
5881 EL_BUG_RIGHT, -1, -1
5884 Xbug_1_s, TRUE, FALSE,
5888 Xbug_1_w, TRUE, FALSE,
5892 Xbug_2_n, FALSE, FALSE,
5896 Xbug_2_e, FALSE, FALSE,
5897 EL_BUG_RIGHT, -1, -1
5900 Xbug_2_s, FALSE, FALSE,
5904 Xbug_2_w, FALSE, FALSE,
5908 Ybug_n, FALSE, FALSE,
5909 EL_BUG, ACTION_MOVING, MV_BIT_UP
5912 Ybug_nB, FALSE, TRUE,
5913 EL_BUG, ACTION_MOVING, MV_BIT_UP
5916 Ybug_e, FALSE, FALSE,
5917 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5920 Ybug_eB, FALSE, TRUE,
5921 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5924 Ybug_s, FALSE, FALSE,
5925 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5928 Ybug_sB, FALSE, TRUE,
5929 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5932 Ybug_w, FALSE, FALSE,
5933 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5936 Ybug_wB, FALSE, TRUE,
5937 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5940 Ybug_w_n, FALSE, FALSE,
5941 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5944 Ybug_n_e, FALSE, FALSE,
5945 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5948 Ybug_e_s, FALSE, FALSE,
5949 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5952 Ybug_s_w, FALSE, FALSE,
5953 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5956 Ybug_e_n, FALSE, FALSE,
5957 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5960 Ybug_s_e, FALSE, FALSE,
5961 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5964 Ybug_w_s, FALSE, FALSE,
5965 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5968 Ybug_n_w, FALSE, FALSE,
5969 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5972 Ybug_stone, FALSE, FALSE,
5973 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5976 Ybug_spring, FALSE, FALSE,
5977 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5980 Xtank_1_n, TRUE, FALSE,
5981 EL_SPACESHIP_UP, -1, -1
5984 Xtank_1_e, TRUE, FALSE,
5985 EL_SPACESHIP_RIGHT, -1, -1
5988 Xtank_1_s, TRUE, FALSE,
5989 EL_SPACESHIP_DOWN, -1, -1
5992 Xtank_1_w, TRUE, FALSE,
5993 EL_SPACESHIP_LEFT, -1, -1
5996 Xtank_2_n, FALSE, FALSE,
5997 EL_SPACESHIP_UP, -1, -1
6000 Xtank_2_e, FALSE, FALSE,
6001 EL_SPACESHIP_RIGHT, -1, -1
6004 Xtank_2_s, FALSE, FALSE,
6005 EL_SPACESHIP_DOWN, -1, -1
6008 Xtank_2_w, FALSE, FALSE,
6009 EL_SPACESHIP_LEFT, -1, -1
6012 Ytank_n, FALSE, FALSE,
6013 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6016 Ytank_nB, FALSE, TRUE,
6017 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6020 Ytank_e, FALSE, FALSE,
6021 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6024 Ytank_eB, FALSE, TRUE,
6025 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6028 Ytank_s, FALSE, FALSE,
6029 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6032 Ytank_sB, FALSE, TRUE,
6033 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6036 Ytank_w, FALSE, FALSE,
6037 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6040 Ytank_wB, FALSE, TRUE,
6041 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6044 Ytank_w_n, FALSE, FALSE,
6045 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6048 Ytank_n_e, FALSE, FALSE,
6049 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6052 Ytank_e_s, FALSE, FALSE,
6053 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6056 Ytank_s_w, FALSE, FALSE,
6057 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6060 Ytank_e_n, FALSE, FALSE,
6061 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6064 Ytank_s_e, FALSE, FALSE,
6065 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6068 Ytank_w_s, FALSE, FALSE,
6069 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6072 Ytank_n_w, FALSE, FALSE,
6073 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6076 Ytank_stone, FALSE, FALSE,
6077 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6080 Ytank_spring, FALSE, FALSE,
6081 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6084 Xandroid, TRUE, FALSE,
6085 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6088 Xandroid_1_n, FALSE, FALSE,
6089 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6092 Xandroid_2_n, FALSE, FALSE,
6093 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6096 Xandroid_1_e, FALSE, FALSE,
6097 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6100 Xandroid_2_e, FALSE, FALSE,
6101 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6104 Xandroid_1_w, FALSE, FALSE,
6105 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6108 Xandroid_2_w, FALSE, FALSE,
6109 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6112 Xandroid_1_s, FALSE, FALSE,
6113 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6116 Xandroid_2_s, FALSE, FALSE,
6117 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6120 Yandroid_n, FALSE, FALSE,
6121 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6124 Yandroid_nB, FALSE, TRUE,
6125 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6128 Yandroid_ne, FALSE, FALSE,
6129 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6132 Yandroid_neB, FALSE, TRUE,
6133 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6136 Yandroid_e, FALSE, FALSE,
6137 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6140 Yandroid_eB, FALSE, TRUE,
6141 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6144 Yandroid_se, FALSE, FALSE,
6145 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6148 Yandroid_seB, FALSE, TRUE,
6149 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6152 Yandroid_s, FALSE, FALSE,
6153 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6156 Yandroid_sB, FALSE, TRUE,
6157 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6160 Yandroid_sw, FALSE, FALSE,
6161 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6164 Yandroid_swB, FALSE, TRUE,
6165 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6168 Yandroid_w, FALSE, FALSE,
6169 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6172 Yandroid_wB, FALSE, TRUE,
6173 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6176 Yandroid_nw, FALSE, FALSE,
6177 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6180 Yandroid_nwB, FALSE, TRUE,
6181 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6184 Xspring, TRUE, FALSE,
6188 Xspring_pause, FALSE, FALSE,
6192 Xspring_e, FALSE, FALSE,
6196 Xspring_w, FALSE, FALSE,
6200 Xspring_fall, FALSE, FALSE,
6204 Yspring_s, FALSE, FALSE,
6205 EL_SPRING, ACTION_FALLING, -1
6208 Yspring_sB, FALSE, TRUE,
6209 EL_SPRING, ACTION_FALLING, -1
6212 Yspring_e, FALSE, FALSE,
6213 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6216 Yspring_eB, FALSE, TRUE,
6217 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6220 Yspring_w, FALSE, FALSE,
6221 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6224 Yspring_wB, FALSE, TRUE,
6225 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6228 Yspring_alien_e, FALSE, FALSE,
6229 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6232 Yspring_alien_eB, FALSE, TRUE,
6233 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6236 Yspring_alien_w, FALSE, FALSE,
6237 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6240 Yspring_alien_wB, FALSE, TRUE,
6241 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6244 Xeater_n, TRUE, FALSE,
6245 EL_YAMYAM_UP, -1, -1
6248 Xeater_e, TRUE, FALSE,
6249 EL_YAMYAM_RIGHT, -1, -1
6252 Xeater_w, TRUE, FALSE,
6253 EL_YAMYAM_LEFT, -1, -1
6256 Xeater_s, TRUE, FALSE,
6257 EL_YAMYAM_DOWN, -1, -1
6260 Yeater_n, FALSE, FALSE,
6261 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6264 Yeater_nB, FALSE, TRUE,
6265 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6268 Yeater_e, FALSE, FALSE,
6269 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6272 Yeater_eB, FALSE, TRUE,
6273 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6276 Yeater_s, FALSE, FALSE,
6277 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6280 Yeater_sB, FALSE, TRUE,
6281 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6284 Yeater_w, FALSE, FALSE,
6285 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6288 Yeater_wB, FALSE, TRUE,
6289 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6292 Yeater_stone, FALSE, FALSE,
6293 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6296 Yeater_spring, FALSE, FALSE,
6297 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6300 Xalien, TRUE, FALSE,
6304 Xalien_pause, FALSE, FALSE,
6308 Yalien_n, FALSE, FALSE,
6309 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6312 Yalien_nB, FALSE, TRUE,
6313 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6316 Yalien_e, FALSE, FALSE,
6317 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6320 Yalien_eB, FALSE, TRUE,
6321 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6324 Yalien_s, FALSE, FALSE,
6325 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6328 Yalien_sB, FALSE, TRUE,
6329 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6332 Yalien_w, FALSE, FALSE,
6333 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6336 Yalien_wB, FALSE, TRUE,
6337 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6340 Yalien_stone, FALSE, FALSE,
6341 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6344 Yalien_spring, FALSE, FALSE,
6345 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6348 Xemerald, TRUE, FALSE,
6352 Xemerald_pause, FALSE, FALSE,
6356 Xemerald_fall, FALSE, FALSE,
6360 Xemerald_shine, FALSE, FALSE,
6361 EL_EMERALD, ACTION_TWINKLING, -1
6364 Yemerald_s, FALSE, FALSE,
6365 EL_EMERALD, ACTION_FALLING, -1
6368 Yemerald_sB, FALSE, TRUE,
6369 EL_EMERALD, ACTION_FALLING, -1
6372 Yemerald_e, FALSE, FALSE,
6373 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6376 Yemerald_eB, FALSE, TRUE,
6377 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6380 Yemerald_w, FALSE, FALSE,
6381 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6384 Yemerald_wB, FALSE, TRUE,
6385 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6388 Yemerald_blank, FALSE, FALSE,
6389 EL_EMERALD, ACTION_COLLECTING, -1
6392 Ynut_stone, FALSE, FALSE,
6393 EL_NUT, ACTION_BREAKING, -1
6396 Xdiamond, TRUE, FALSE,
6400 Xdiamond_pause, FALSE, FALSE,
6404 Xdiamond_fall, FALSE, FALSE,
6408 Xdiamond_shine, FALSE, FALSE,
6409 EL_DIAMOND, ACTION_TWINKLING, -1
6412 Ydiamond_s, FALSE, FALSE,
6413 EL_DIAMOND, ACTION_FALLING, -1
6416 Ydiamond_sB, FALSE, TRUE,
6417 EL_DIAMOND, ACTION_FALLING, -1
6420 Ydiamond_e, FALSE, FALSE,
6421 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6424 Ydiamond_eB, FALSE, TRUE,
6425 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6428 Ydiamond_w, FALSE, FALSE,
6429 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6432 Ydiamond_wB, FALSE, TRUE,
6433 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6436 Ydiamond_blank, FALSE, FALSE,
6437 EL_DIAMOND, ACTION_COLLECTING, -1
6440 Ydiamond_stone, FALSE, FALSE,
6441 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6444 Xdrip_fall, TRUE, FALSE,
6445 EL_AMOEBA_DROP, -1, -1
6448 Xdrip_stretch, FALSE, FALSE,
6449 EL_AMOEBA_DROP, ACTION_FALLING, -1
6452 Xdrip_stretchB, FALSE, TRUE,
6453 EL_AMOEBA_DROP, ACTION_FALLING, -1
6456 Xdrip, FALSE, FALSE,
6457 EL_AMOEBA_DROP, ACTION_GROWING, -1
6460 Ydrip_1_s, FALSE, FALSE,
6461 EL_AMOEBA_DROP, ACTION_FALLING, -1
6464 Ydrip_1_sB, FALSE, TRUE,
6465 EL_AMOEBA_DROP, ACTION_FALLING, -1
6468 Ydrip_2_s, FALSE, FALSE,
6469 EL_AMOEBA_DROP, ACTION_FALLING, -1
6472 Ydrip_2_sB, FALSE, TRUE,
6473 EL_AMOEBA_DROP, ACTION_FALLING, -1
6480 Xbomb_pause, FALSE, FALSE,
6484 Xbomb_fall, FALSE, FALSE,
6488 Ybomb_s, FALSE, FALSE,
6489 EL_BOMB, ACTION_FALLING, -1
6492 Ybomb_sB, FALSE, TRUE,
6493 EL_BOMB, ACTION_FALLING, -1
6496 Ybomb_e, FALSE, FALSE,
6497 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6500 Ybomb_eB, FALSE, TRUE,
6501 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6504 Ybomb_w, FALSE, FALSE,
6505 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6508 Ybomb_wB, FALSE, TRUE,
6509 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6512 Ybomb_blank, FALSE, FALSE,
6513 EL_BOMB, ACTION_ACTIVATING, -1
6516 Xballoon, TRUE, FALSE,
6520 Yballoon_n, FALSE, FALSE,
6521 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6524 Yballoon_nB, FALSE, TRUE,
6525 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6528 Yballoon_e, FALSE, FALSE,
6529 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6532 Yballoon_eB, FALSE, TRUE,
6533 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6536 Yballoon_s, FALSE, FALSE,
6537 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6540 Yballoon_sB, FALSE, TRUE,
6541 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6544 Yballoon_w, FALSE, FALSE,
6545 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6548 Yballoon_wB, FALSE, TRUE,
6549 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6552 Xgrass, TRUE, FALSE,
6553 EL_EMC_GRASS, -1, -1
6556 Ygrass_nB, FALSE, FALSE,
6557 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6560 Ygrass_eB, FALSE, FALSE,
6561 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6564 Ygrass_sB, FALSE, FALSE,
6565 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6568 Ygrass_wB, FALSE, FALSE,
6569 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6576 Ydirt_nB, FALSE, FALSE,
6577 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6580 Ydirt_eB, FALSE, FALSE,
6581 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6584 Ydirt_sB, FALSE, FALSE,
6585 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6588 Ydirt_wB, FALSE, FALSE,
6589 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6592 Xacid_ne, TRUE, FALSE,
6593 EL_ACID_POOL_TOPRIGHT, -1, -1
6596 Xacid_se, TRUE, FALSE,
6597 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6600 Xacid_s, TRUE, FALSE,
6601 EL_ACID_POOL_BOTTOM, -1, -1
6604 Xacid_sw, TRUE, FALSE,
6605 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6608 Xacid_nw, TRUE, FALSE,
6609 EL_ACID_POOL_TOPLEFT, -1, -1
6612 Xacid_1, TRUE, FALSE,
6616 Xacid_2, FALSE, FALSE,
6620 Xacid_3, FALSE, FALSE,
6624 Xacid_4, FALSE, FALSE,
6628 Xacid_5, FALSE, FALSE,
6632 Xacid_6, FALSE, FALSE,
6636 Xacid_7, FALSE, FALSE,
6640 Xacid_8, FALSE, FALSE,
6644 Xball_1, TRUE, FALSE,
6645 EL_EMC_MAGIC_BALL, -1, -1
6648 Yball_1, FALSE, FALSE,
6649 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6652 Xball_2, FALSE, FALSE,
6653 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6656 Yball_2, FALSE, FALSE,
6657 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6660 Yball_blank, FALSE, FALSE,
6661 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6664 Ykey_1_blank, FALSE, FALSE,
6665 EL_EM_KEY_1, ACTION_COLLECTING, -1
6668 Ykey_2_blank, FALSE, FALSE,
6669 EL_EM_KEY_2, ACTION_COLLECTING, -1
6672 Ykey_3_blank, FALSE, FALSE,
6673 EL_EM_KEY_3, ACTION_COLLECTING, -1
6676 Ykey_4_blank, FALSE, FALSE,
6677 EL_EM_KEY_4, ACTION_COLLECTING, -1
6680 Ykey_5_blank, FALSE, FALSE,
6681 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6684 Ykey_6_blank, FALSE, FALSE,
6685 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6688 Ykey_7_blank, FALSE, FALSE,
6689 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6692 Ykey_8_blank, FALSE, FALSE,
6693 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6696 Ylenses_blank, FALSE, FALSE,
6697 EL_EMC_LENSES, ACTION_COLLECTING, -1
6700 Ymagnify_blank, FALSE, FALSE,
6701 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6704 Ygrass_blank, FALSE, FALSE,
6705 EL_EMC_GRASS, ACTION_SNAPPING, -1
6708 Ydirt_blank, FALSE, FALSE,
6709 EL_SAND, ACTION_SNAPPING, -1
6712 Xslidewall_ns, TRUE, FALSE,
6713 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6716 Yslidewall_ns_blank, FALSE, FALSE,
6717 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6720 Xslidewall_ew, TRUE, FALSE,
6721 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6724 Yslidewall_ew_blank, FALSE, FALSE,
6725 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6728 Xwonderwall, TRUE, FALSE,
6729 EL_MAGIC_WALL, -1, -1
6732 XwonderwallB, FALSE, FALSE,
6733 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6736 Xamoeba_1, TRUE, FALSE,
6737 EL_AMOEBA_DRY, ACTION_OTHER, -1
6740 Xamoeba_2, FALSE, FALSE,
6741 EL_AMOEBA_DRY, ACTION_OTHER, -1
6744 Xamoeba_3, FALSE, FALSE,
6745 EL_AMOEBA_DRY, ACTION_OTHER, -1
6748 Xamoeba_4, FALSE, FALSE,
6749 EL_AMOEBA_DRY, ACTION_OTHER, -1
6752 Xamoeba_5, TRUE, FALSE,
6753 EL_AMOEBA_WET, ACTION_OTHER, -1
6756 Xamoeba_6, FALSE, FALSE,
6757 EL_AMOEBA_WET, ACTION_OTHER, -1
6760 Xamoeba_7, FALSE, FALSE,
6761 EL_AMOEBA_WET, ACTION_OTHER, -1
6764 Xamoeba_8, FALSE, FALSE,
6765 EL_AMOEBA_WET, ACTION_OTHER, -1
6768 Xdoor_1, TRUE, FALSE,
6769 EL_EM_GATE_1, -1, -1
6772 Xdoor_2, TRUE, FALSE,
6773 EL_EM_GATE_2, -1, -1
6776 Xdoor_3, TRUE, FALSE,
6777 EL_EM_GATE_3, -1, -1
6780 Xdoor_4, TRUE, FALSE,
6781 EL_EM_GATE_4, -1, -1
6784 Xdoor_5, TRUE, FALSE,
6785 EL_EMC_GATE_5, -1, -1
6788 Xdoor_6, TRUE, FALSE,
6789 EL_EMC_GATE_6, -1, -1
6792 Xdoor_7, TRUE, FALSE,
6793 EL_EMC_GATE_7, -1, -1
6796 Xdoor_8, TRUE, FALSE,
6797 EL_EMC_GATE_8, -1, -1
6800 Xkey_1, TRUE, FALSE,
6804 Xkey_2, TRUE, FALSE,
6808 Xkey_3, TRUE, FALSE,
6812 Xkey_4, TRUE, FALSE,
6816 Xkey_5, TRUE, FALSE,
6817 EL_EMC_KEY_5, -1, -1
6820 Xkey_6, TRUE, FALSE,
6821 EL_EMC_KEY_6, -1, -1
6824 Xkey_7, TRUE, FALSE,
6825 EL_EMC_KEY_7, -1, -1
6828 Xkey_8, TRUE, FALSE,
6829 EL_EMC_KEY_8, -1, -1
6832 Xwind_n, TRUE, FALSE,
6833 EL_BALLOON_SWITCH_UP, -1, -1
6836 Xwind_e, TRUE, FALSE,
6837 EL_BALLOON_SWITCH_RIGHT, -1, -1
6840 Xwind_s, TRUE, FALSE,
6841 EL_BALLOON_SWITCH_DOWN, -1, -1
6844 Xwind_w, TRUE, FALSE,
6845 EL_BALLOON_SWITCH_LEFT, -1, -1
6848 Xwind_any, TRUE, FALSE,
6849 EL_BALLOON_SWITCH_ANY, -1, -1
6852 Xwind_stop, TRUE, FALSE,
6853 EL_BALLOON_SWITCH_NONE, -1, -1
6857 EL_EM_EXIT_CLOSED, -1, -1
6860 Xexit_1, TRUE, FALSE,
6861 EL_EM_EXIT_OPEN, -1, -1
6864 Xexit_2, FALSE, FALSE,
6865 EL_EM_EXIT_OPEN, -1, -1
6868 Xexit_3, FALSE, FALSE,
6869 EL_EM_EXIT_OPEN, -1, -1
6872 Xdynamite, TRUE, FALSE,
6873 EL_EM_DYNAMITE, -1, -1
6876 Ydynamite_blank, FALSE, FALSE,
6877 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6880 Xdynamite_1, TRUE, FALSE,
6881 EL_EM_DYNAMITE_ACTIVE, -1, -1
6884 Xdynamite_2, FALSE, FALSE,
6885 EL_EM_DYNAMITE_ACTIVE, -1, -1
6888 Xdynamite_3, FALSE, FALSE,
6889 EL_EM_DYNAMITE_ACTIVE, -1, -1
6892 Xdynamite_4, FALSE, FALSE,
6893 EL_EM_DYNAMITE_ACTIVE, -1, -1
6896 Xbumper, TRUE, FALSE,
6897 EL_EMC_SPRING_BUMPER, -1, -1
6900 XbumperB, FALSE, FALSE,
6901 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6904 Xwheel, TRUE, FALSE,
6905 EL_ROBOT_WHEEL, -1, -1
6908 XwheelB, FALSE, FALSE,
6909 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6912 Xswitch, TRUE, FALSE,
6913 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6916 XswitchB, FALSE, FALSE,
6917 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6921 EL_QUICKSAND_EMPTY, -1, -1
6924 Xsand_stone, TRUE, FALSE,
6925 EL_QUICKSAND_FULL, -1, -1
6928 Xsand_stonein_1, FALSE, TRUE,
6929 EL_ROCK, ACTION_FILLING, -1
6932 Xsand_stonein_2, FALSE, TRUE,
6933 EL_ROCK, ACTION_FILLING, -1
6936 Xsand_stonein_3, FALSE, TRUE,
6937 EL_ROCK, ACTION_FILLING, -1
6940 Xsand_stonein_4, FALSE, TRUE,
6941 EL_ROCK, ACTION_FILLING, -1
6944 Xsand_stonesand_1, FALSE, FALSE,
6945 EL_QUICKSAND_EMPTYING, -1, -1
6948 Xsand_stonesand_2, FALSE, FALSE,
6949 EL_QUICKSAND_EMPTYING, -1, -1
6952 Xsand_stonesand_3, FALSE, FALSE,
6953 EL_QUICKSAND_EMPTYING, -1, -1
6956 Xsand_stonesand_4, FALSE, FALSE,
6957 EL_QUICKSAND_EMPTYING, -1, -1
6960 Xsand_stonesand_quickout_1, FALSE, FALSE,
6961 EL_QUICKSAND_EMPTYING, -1, -1
6964 Xsand_stonesand_quickout_2, FALSE, FALSE,
6965 EL_QUICKSAND_EMPTYING, -1, -1
6968 Xsand_stoneout_1, FALSE, FALSE,
6969 EL_ROCK, ACTION_EMPTYING, -1
6972 Xsand_stoneout_2, FALSE, FALSE,
6973 EL_ROCK, ACTION_EMPTYING, -1
6976 Xsand_sandstone_1, FALSE, FALSE,
6977 EL_QUICKSAND_FILLING, -1, -1
6980 Xsand_sandstone_2, FALSE, FALSE,
6981 EL_QUICKSAND_FILLING, -1, -1
6984 Xsand_sandstone_3, FALSE, FALSE,
6985 EL_QUICKSAND_FILLING, -1, -1
6988 Xsand_sandstone_4, FALSE, FALSE,
6989 EL_QUICKSAND_FILLING, -1, -1
6992 Xplant, TRUE, FALSE,
6993 EL_EMC_PLANT, -1, -1
6996 Yplant, FALSE, FALSE,
6997 EL_EMC_PLANT, -1, -1
7000 Xlenses, TRUE, FALSE,
7001 EL_EMC_LENSES, -1, -1
7004 Xmagnify, TRUE, FALSE,
7005 EL_EMC_MAGNIFIER, -1, -1
7008 Xfake_amoeba, TRUE, FALSE,
7009 EL_EMC_DRIPPER, -1, -1
7012 Xfake_amoebaB, FALSE, FALSE,
7013 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7016 Xfake_blank, TRUE, FALSE,
7017 EL_INVISIBLE_WALL, -1, -1
7020 Xfake_blankB, FALSE, FALSE,
7021 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7024 Xfake_grass, TRUE, FALSE,
7025 EL_EMC_FAKE_GRASS, -1, -1
7028 Xfake_grassB, FALSE, FALSE,
7029 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7032 Xfake_door_1, TRUE, FALSE,
7033 EL_EM_GATE_1_GRAY, -1, -1
7036 Xfake_door_2, TRUE, FALSE,
7037 EL_EM_GATE_2_GRAY, -1, -1
7040 Xfake_door_3, TRUE, FALSE,
7041 EL_EM_GATE_3_GRAY, -1, -1
7044 Xfake_door_4, TRUE, FALSE,
7045 EL_EM_GATE_4_GRAY, -1, -1
7048 Xfake_door_5, TRUE, FALSE,
7049 EL_EMC_GATE_5_GRAY, -1, -1
7052 Xfake_door_6, TRUE, FALSE,
7053 EL_EMC_GATE_6_GRAY, -1, -1
7056 Xfake_door_7, TRUE, FALSE,
7057 EL_EMC_GATE_7_GRAY, -1, -1
7060 Xfake_door_8, TRUE, FALSE,
7061 EL_EMC_GATE_8_GRAY, -1, -1
7064 Xfake_acid_1, TRUE, FALSE,
7065 EL_EMC_FAKE_ACID, -1, -1
7068 Xfake_acid_2, FALSE, FALSE,
7069 EL_EMC_FAKE_ACID, -1, -1
7072 Xfake_acid_3, FALSE, FALSE,
7073 EL_EMC_FAKE_ACID, -1, -1
7076 Xfake_acid_4, FALSE, FALSE,
7077 EL_EMC_FAKE_ACID, -1, -1
7080 Xfake_acid_5, FALSE, FALSE,
7081 EL_EMC_FAKE_ACID, -1, -1
7084 Xfake_acid_6, FALSE, FALSE,
7085 EL_EMC_FAKE_ACID, -1, -1
7088 Xfake_acid_7, FALSE, FALSE,
7089 EL_EMC_FAKE_ACID, -1, -1
7092 Xfake_acid_8, FALSE, FALSE,
7093 EL_EMC_FAKE_ACID, -1, -1
7096 Xsteel_1, TRUE, FALSE,
7097 EL_STEELWALL, -1, -1
7100 Xsteel_2, TRUE, FALSE,
7101 EL_EMC_STEELWALL_2, -1, -1
7104 Xsteel_3, TRUE, FALSE,
7105 EL_EMC_STEELWALL_3, -1, -1
7108 Xsteel_4, TRUE, FALSE,
7109 EL_EMC_STEELWALL_4, -1, -1
7112 Xwall_1, TRUE, FALSE,
7116 Xwall_2, TRUE, FALSE,
7117 EL_EMC_WALL_14, -1, -1
7120 Xwall_3, TRUE, FALSE,
7121 EL_EMC_WALL_15, -1, -1
7124 Xwall_4, TRUE, FALSE,
7125 EL_EMC_WALL_16, -1, -1
7128 Xroundwall_1, TRUE, FALSE,
7129 EL_WALL_SLIPPERY, -1, -1
7132 Xroundwall_2, TRUE, FALSE,
7133 EL_EMC_WALL_SLIPPERY_2, -1, -1
7136 Xroundwall_3, TRUE, FALSE,
7137 EL_EMC_WALL_SLIPPERY_3, -1, -1
7140 Xroundwall_4, TRUE, FALSE,
7141 EL_EMC_WALL_SLIPPERY_4, -1, -1
7144 Xdecor_1, TRUE, FALSE,
7145 EL_EMC_WALL_8, -1, -1
7148 Xdecor_2, TRUE, FALSE,
7149 EL_EMC_WALL_6, -1, -1
7152 Xdecor_3, TRUE, FALSE,
7153 EL_EMC_WALL_4, -1, -1
7156 Xdecor_4, TRUE, FALSE,
7157 EL_EMC_WALL_7, -1, -1
7160 Xdecor_5, TRUE, FALSE,
7161 EL_EMC_WALL_5, -1, -1
7164 Xdecor_6, TRUE, FALSE,
7165 EL_EMC_WALL_9, -1, -1
7168 Xdecor_7, TRUE, FALSE,
7169 EL_EMC_WALL_10, -1, -1
7172 Xdecor_8, TRUE, FALSE,
7173 EL_EMC_WALL_1, -1, -1
7176 Xdecor_9, TRUE, FALSE,
7177 EL_EMC_WALL_2, -1, -1
7180 Xdecor_10, TRUE, FALSE,
7181 EL_EMC_WALL_3, -1, -1
7184 Xdecor_11, TRUE, FALSE,
7185 EL_EMC_WALL_11, -1, -1
7188 Xdecor_12, TRUE, FALSE,
7189 EL_EMC_WALL_12, -1, -1
7192 Xalpha_0, TRUE, FALSE,
7193 EL_CHAR('0'), -1, -1
7196 Xalpha_1, TRUE, FALSE,
7197 EL_CHAR('1'), -1, -1
7200 Xalpha_2, TRUE, FALSE,
7201 EL_CHAR('2'), -1, -1
7204 Xalpha_3, TRUE, FALSE,
7205 EL_CHAR('3'), -1, -1
7208 Xalpha_4, TRUE, FALSE,
7209 EL_CHAR('4'), -1, -1
7212 Xalpha_5, TRUE, FALSE,
7213 EL_CHAR('5'), -1, -1
7216 Xalpha_6, TRUE, FALSE,
7217 EL_CHAR('6'), -1, -1
7220 Xalpha_7, TRUE, FALSE,
7221 EL_CHAR('7'), -1, -1
7224 Xalpha_8, TRUE, FALSE,
7225 EL_CHAR('8'), -1, -1
7228 Xalpha_9, TRUE, FALSE,
7229 EL_CHAR('9'), -1, -1
7232 Xalpha_excla, TRUE, FALSE,
7233 EL_CHAR('!'), -1, -1
7236 Xalpha_quote, TRUE, FALSE,
7237 EL_CHAR('"'), -1, -1
7240 Xalpha_comma, TRUE, FALSE,
7241 EL_CHAR(','), -1, -1
7244 Xalpha_minus, TRUE, FALSE,
7245 EL_CHAR('-'), -1, -1
7248 Xalpha_perio, TRUE, FALSE,
7249 EL_CHAR('.'), -1, -1
7252 Xalpha_colon, TRUE, FALSE,
7253 EL_CHAR(':'), -1, -1
7256 Xalpha_quest, TRUE, FALSE,
7257 EL_CHAR('?'), -1, -1
7260 Xalpha_a, TRUE, FALSE,
7261 EL_CHAR('A'), -1, -1
7264 Xalpha_b, TRUE, FALSE,
7265 EL_CHAR('B'), -1, -1
7268 Xalpha_c, TRUE, FALSE,
7269 EL_CHAR('C'), -1, -1
7272 Xalpha_d, TRUE, FALSE,
7273 EL_CHAR('D'), -1, -1
7276 Xalpha_e, TRUE, FALSE,
7277 EL_CHAR('E'), -1, -1
7280 Xalpha_f, TRUE, FALSE,
7281 EL_CHAR('F'), -1, -1
7284 Xalpha_g, TRUE, FALSE,
7285 EL_CHAR('G'), -1, -1
7288 Xalpha_h, TRUE, FALSE,
7289 EL_CHAR('H'), -1, -1
7292 Xalpha_i, TRUE, FALSE,
7293 EL_CHAR('I'), -1, -1
7296 Xalpha_j, TRUE, FALSE,
7297 EL_CHAR('J'), -1, -1
7300 Xalpha_k, TRUE, FALSE,
7301 EL_CHAR('K'), -1, -1
7304 Xalpha_l, TRUE, FALSE,
7305 EL_CHAR('L'), -1, -1
7308 Xalpha_m, TRUE, FALSE,
7309 EL_CHAR('M'), -1, -1
7312 Xalpha_n, TRUE, FALSE,
7313 EL_CHAR('N'), -1, -1
7316 Xalpha_o, TRUE, FALSE,
7317 EL_CHAR('O'), -1, -1
7320 Xalpha_p, TRUE, FALSE,
7321 EL_CHAR('P'), -1, -1
7324 Xalpha_q, TRUE, FALSE,
7325 EL_CHAR('Q'), -1, -1
7328 Xalpha_r, TRUE, FALSE,
7329 EL_CHAR('R'), -1, -1
7332 Xalpha_s, TRUE, FALSE,
7333 EL_CHAR('S'), -1, -1
7336 Xalpha_t, TRUE, FALSE,
7337 EL_CHAR('T'), -1, -1
7340 Xalpha_u, TRUE, FALSE,
7341 EL_CHAR('U'), -1, -1
7344 Xalpha_v, TRUE, FALSE,
7345 EL_CHAR('V'), -1, -1
7348 Xalpha_w, TRUE, FALSE,
7349 EL_CHAR('W'), -1, -1
7352 Xalpha_x, TRUE, FALSE,
7353 EL_CHAR('X'), -1, -1
7356 Xalpha_y, TRUE, FALSE,
7357 EL_CHAR('Y'), -1, -1
7360 Xalpha_z, TRUE, FALSE,
7361 EL_CHAR('Z'), -1, -1
7364 Xalpha_arrow_e, TRUE, FALSE,
7365 EL_CHAR('>'), -1, -1
7368 Xalpha_arrow_w, TRUE, FALSE,
7369 EL_CHAR('<'), -1, -1
7372 Xalpha_copyr, TRUE, FALSE,
7373 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7377 Xboom_bug, FALSE, FALSE,
7378 EL_BUG, ACTION_EXPLODING, -1
7381 Xboom_bomb, FALSE, FALSE,
7382 EL_BOMB, ACTION_EXPLODING, -1
7385 Xboom_android, FALSE, FALSE,
7386 EL_EMC_ANDROID, ACTION_OTHER, -1
7389 Xboom_1, FALSE, FALSE,
7390 EL_DEFAULT, ACTION_EXPLODING, -1
7393 Xboom_2, FALSE, FALSE,
7394 EL_DEFAULT, ACTION_EXPLODING, -1
7397 Znormal, FALSE, FALSE,
7401 Zdynamite, FALSE, FALSE,
7405 Zplayer, FALSE, FALSE,
7409 Zborder, FALSE, FALSE,
7419 static struct Mapping_EM_to_RND_player
7428 em_player_mapping_list[] =
7432 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7436 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7440 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7444 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7448 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7452 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7456 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7460 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7464 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7468 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7472 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7476 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7480 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7484 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7488 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7492 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7496 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7500 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7504 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7508 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7512 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7516 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7520 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7524 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7528 EL_PLAYER_1, ACTION_DEFAULT, -1,
7532 EL_PLAYER_2, ACTION_DEFAULT, -1,
7536 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7540 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7544 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7548 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7552 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7556 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7560 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7564 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7568 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7572 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7576 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7580 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7584 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7588 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7592 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7596 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7600 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7604 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7608 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7612 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7616 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7620 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7624 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7628 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7632 EL_PLAYER_3, ACTION_DEFAULT, -1,
7636 EL_PLAYER_4, ACTION_DEFAULT, -1,
7645 int map_element_RND_to_EM(int element_rnd)
7647 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7648 static boolean mapping_initialized = FALSE;
7650 if (!mapping_initialized)
7654 // return "Xalpha_quest" for all undefined elements in mapping array
7655 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7656 mapping_RND_to_EM[i] = Xalpha_quest;
7658 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7659 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7660 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7661 em_object_mapping_list[i].element_em;
7663 mapping_initialized = TRUE;
7666 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7667 return mapping_RND_to_EM[element_rnd];
7669 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7674 int map_element_EM_to_RND(int element_em)
7676 static unsigned short mapping_EM_to_RND[TILE_MAX];
7677 static boolean mapping_initialized = FALSE;
7679 if (!mapping_initialized)
7683 // return "EL_UNKNOWN" for all undefined elements in mapping array
7684 for (i = 0; i < TILE_MAX; i++)
7685 mapping_EM_to_RND[i] = EL_UNKNOWN;
7687 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7688 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7689 em_object_mapping_list[i].element_rnd;
7691 mapping_initialized = TRUE;
7694 if (element_em >= 0 && element_em < TILE_MAX)
7695 return mapping_EM_to_RND[element_em];
7697 Error(ERR_WARN, "invalid EM level element %d", element_em);
7702 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7704 struct LevelInfo_EM *level_em = level->native_em_level;
7705 struct LEVEL *lev = level_em->lev;
7708 for (i = 0; i < TILE_MAX; i++)
7709 lev->android_array[i] = Xblank;
7711 for (i = 0; i < level->num_android_clone_elements; i++)
7713 int element_rnd = level->android_clone_element[i];
7714 int element_em = map_element_RND_to_EM(element_rnd);
7716 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7717 if (em_object_mapping_list[j].element_rnd == element_rnd)
7718 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7722 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7724 struct LevelInfo_EM *level_em = level->native_em_level;
7725 struct LEVEL *lev = level_em->lev;
7728 level->num_android_clone_elements = 0;
7730 for (i = 0; i < TILE_MAX; i++)
7732 int element_em = lev->android_array[i];
7734 boolean element_found = FALSE;
7736 if (element_em == Xblank)
7739 element_rnd = map_element_EM_to_RND(element_em);
7741 for (j = 0; j < level->num_android_clone_elements; j++)
7742 if (level->android_clone_element[j] == element_rnd)
7743 element_found = TRUE;
7747 level->android_clone_element[level->num_android_clone_elements++] =
7750 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7755 if (level->num_android_clone_elements == 0)
7757 level->num_android_clone_elements = 1;
7758 level->android_clone_element[0] = EL_EMPTY;
7762 int map_direction_RND_to_EM(int direction)
7764 return (direction == MV_UP ? 0 :
7765 direction == MV_RIGHT ? 1 :
7766 direction == MV_DOWN ? 2 :
7767 direction == MV_LEFT ? 3 :
7771 int map_direction_EM_to_RND(int direction)
7773 return (direction == 0 ? MV_UP :
7774 direction == 1 ? MV_RIGHT :
7775 direction == 2 ? MV_DOWN :
7776 direction == 3 ? MV_LEFT :
7780 int map_element_RND_to_SP(int element_rnd)
7782 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7784 if (element_rnd >= EL_SP_START &&
7785 element_rnd <= EL_SP_END)
7786 element_sp = element_rnd - EL_SP_START;
7787 else if (element_rnd == EL_EMPTY_SPACE)
7789 else if (element_rnd == EL_INVISIBLE_WALL)
7795 int map_element_SP_to_RND(int element_sp)
7797 int element_rnd = EL_UNKNOWN;
7799 if (element_sp >= 0x00 &&
7801 element_rnd = EL_SP_START + element_sp;
7802 else if (element_sp == 0x28)
7803 element_rnd = EL_INVISIBLE_WALL;
7808 int map_action_SP_to_RND(int action_sp)
7812 case actActive: return ACTION_ACTIVE;
7813 case actImpact: return ACTION_IMPACT;
7814 case actExploding: return ACTION_EXPLODING;
7815 case actDigging: return ACTION_DIGGING;
7816 case actSnapping: return ACTION_SNAPPING;
7817 case actCollecting: return ACTION_COLLECTING;
7818 case actPassing: return ACTION_PASSING;
7819 case actPushing: return ACTION_PUSHING;
7820 case actDropping: return ACTION_DROPPING;
7822 default: return ACTION_DEFAULT;
7826 int map_element_RND_to_MM(int element_rnd)
7828 return (element_rnd >= EL_MM_START_1 &&
7829 element_rnd <= EL_MM_END_1 ?
7830 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7832 element_rnd >= EL_MM_START_2 &&
7833 element_rnd <= EL_MM_END_2 ?
7834 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7836 element_rnd >= EL_CHAR_START &&
7837 element_rnd <= EL_CHAR_END ?
7838 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7840 element_rnd >= EL_MM_RUNTIME_START &&
7841 element_rnd <= EL_MM_RUNTIME_END ?
7842 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7844 element_rnd >= EL_MM_DUMMY_START &&
7845 element_rnd <= EL_MM_DUMMY_END ?
7846 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7848 EL_MM_EMPTY_NATIVE);
7851 int map_element_MM_to_RND(int element_mm)
7853 return (element_mm == EL_MM_EMPTY_NATIVE ||
7854 element_mm == EL_DF_EMPTY_NATIVE ?
7857 element_mm >= EL_MM_START_1_NATIVE &&
7858 element_mm <= EL_MM_END_1_NATIVE ?
7859 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7861 element_mm >= EL_MM_START_2_NATIVE &&
7862 element_mm <= EL_MM_END_2_NATIVE ?
7863 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7865 element_mm >= EL_MM_CHAR_START_NATIVE &&
7866 element_mm <= EL_MM_CHAR_END_NATIVE ?
7867 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7869 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7870 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7871 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7873 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7874 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7875 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7880 int map_action_MM_to_RND(int action_mm)
7882 // all MM actions are defined to exactly match their RND counterparts
7886 int map_sound_MM_to_RND(int sound_mm)
7890 case SND_MM_GAME_LEVELTIME_CHARGING:
7891 return SND_GAME_LEVELTIME_CHARGING;
7893 case SND_MM_GAME_HEALTH_CHARGING:
7894 return SND_GAME_HEALTH_CHARGING;
7897 return SND_UNDEFINED;
7901 int map_mm_wall_element(int element)
7903 return (element >= EL_MM_STEEL_WALL_START &&
7904 element <= EL_MM_STEEL_WALL_END ?
7907 element >= EL_MM_WOODEN_WALL_START &&
7908 element <= EL_MM_WOODEN_WALL_END ?
7911 element >= EL_MM_ICE_WALL_START &&
7912 element <= EL_MM_ICE_WALL_END ?
7915 element >= EL_MM_AMOEBA_WALL_START &&
7916 element <= EL_MM_AMOEBA_WALL_END ?
7919 element >= EL_DF_STEEL_WALL_START &&
7920 element <= EL_DF_STEEL_WALL_END ?
7923 element >= EL_DF_WOODEN_WALL_START &&
7924 element <= EL_DF_WOODEN_WALL_END ?
7930 int map_mm_wall_element_editor(int element)
7934 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7935 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7936 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7937 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7938 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7939 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7941 default: return element;
7945 int get_next_element(int element)
7949 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7950 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7951 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7952 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7953 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7954 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7955 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7956 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7957 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7958 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7959 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7961 default: return element;
7965 int el2img_mm(int element_mm)
7967 return el2img(map_element_MM_to_RND(element_mm));
7970 int el_act_dir2img(int element, int action, int direction)
7972 element = GFX_ELEMENT(element);
7973 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7975 // direction_graphic[][] == graphic[] for undefined direction graphics
7976 return element_info[element].direction_graphic[action][direction];
7979 static int el_act_dir2crm(int element, int action, int direction)
7981 element = GFX_ELEMENT(element);
7982 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7984 // direction_graphic[][] == graphic[] for undefined direction graphics
7985 return element_info[element].direction_crumbled[action][direction];
7988 int el_act2img(int element, int action)
7990 element = GFX_ELEMENT(element);
7992 return element_info[element].graphic[action];
7995 int el_act2crm(int element, int action)
7997 element = GFX_ELEMENT(element);
7999 return element_info[element].crumbled[action];
8002 int el_dir2img(int element, int direction)
8004 element = GFX_ELEMENT(element);
8006 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8009 int el2baseimg(int element)
8011 return element_info[element].graphic[ACTION_DEFAULT];
8014 int el2img(int element)
8016 element = GFX_ELEMENT(element);
8018 return element_info[element].graphic[ACTION_DEFAULT];
8021 int el2edimg(int element)
8023 element = GFX_ELEMENT(element);
8025 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8028 int el2preimg(int element)
8030 element = GFX_ELEMENT(element);
8032 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8035 int el2panelimg(int element)
8037 element = GFX_ELEMENT(element);
8039 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8042 int font2baseimg(int font_nr)
8044 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8047 int getBeltNrFromBeltElement(int element)
8049 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8050 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8051 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8054 int getBeltNrFromBeltActiveElement(int element)
8056 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8057 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8058 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8061 int getBeltNrFromBeltSwitchElement(int element)
8063 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8064 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8065 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8068 int getBeltDirNrFromBeltElement(int element)
8070 static int belt_base_element[4] =
8072 EL_CONVEYOR_BELT_1_LEFT,
8073 EL_CONVEYOR_BELT_2_LEFT,
8074 EL_CONVEYOR_BELT_3_LEFT,
8075 EL_CONVEYOR_BELT_4_LEFT
8078 int belt_nr = getBeltNrFromBeltElement(element);
8079 int belt_dir_nr = element - belt_base_element[belt_nr];
8081 return (belt_dir_nr % 3);
8084 int getBeltDirNrFromBeltSwitchElement(int element)
8086 static int belt_base_element[4] =
8088 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8089 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8090 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8091 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8094 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8095 int belt_dir_nr = element - belt_base_element[belt_nr];
8097 return (belt_dir_nr % 3);
8100 int getBeltDirFromBeltElement(int element)
8102 static int belt_move_dir[3] =
8109 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8111 return belt_move_dir[belt_dir_nr];
8114 int getBeltDirFromBeltSwitchElement(int element)
8116 static int belt_move_dir[3] =
8123 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8125 return belt_move_dir[belt_dir_nr];
8128 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8130 static int belt_base_element[4] =
8132 EL_CONVEYOR_BELT_1_LEFT,
8133 EL_CONVEYOR_BELT_2_LEFT,
8134 EL_CONVEYOR_BELT_3_LEFT,
8135 EL_CONVEYOR_BELT_4_LEFT
8138 return belt_base_element[belt_nr] + belt_dir_nr;
8141 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8143 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8145 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8148 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8150 static int belt_base_element[4] =
8152 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8153 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8154 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8155 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8158 return belt_base_element[belt_nr] + belt_dir_nr;
8161 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8163 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8165 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8168 boolean getTeamMode_EM(void)
8170 return game.team_mode || network_playing;
8173 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8175 int game_frame_delay_value;
8177 game_frame_delay_value =
8178 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8179 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8182 if (tape.playing && tape.warp_forward && !tape.pausing)
8183 game_frame_delay_value = 0;
8185 return game_frame_delay_value;
8188 unsigned int InitRND(int seed)
8190 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8191 return InitEngineRandom_EM(seed);
8192 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8193 return InitEngineRandom_SP(seed);
8194 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8195 return InitEngineRandom_MM(seed);
8197 return InitEngineRandom_RND(seed);
8200 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8201 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
8203 static int get_effective_element_EM(int tile, int frame_em)
8205 int element = object_mapping[tile].element_rnd;
8206 int action = object_mapping[tile].action;
8207 boolean is_backside = object_mapping[tile].is_backside;
8208 boolean action_removing = (action == ACTION_DIGGING ||
8209 action == ACTION_SNAPPING ||
8210 action == ACTION_COLLECTING);
8216 case Xacid_splash_e:
8217 case Xacid_splash_w:
8218 return (frame_em > 5 ? EL_EMPTY : element);
8224 else // frame_em == 7
8228 case Xacid_splash_e:
8229 case Xacid_splash_w:
8235 case Ydiamond_stone:
8239 case Xdrip_stretchB:
8255 case Ymagnify_blank:
8258 case Xsand_stonein_1:
8259 case Xsand_stonein_2:
8260 case Xsand_stonein_3:
8261 case Xsand_stonein_4:
8265 return (is_backside || action_removing ? EL_EMPTY : element);
8270 static boolean check_linear_animation_EM(int tile)
8274 case Xsand_stonesand_1:
8275 case Xsand_stonesand_quickout_1:
8276 case Xsand_sandstone_1:
8277 case Xsand_stonein_1:
8278 case Xsand_stoneout_1:
8297 case Xacid_splash_e:
8298 case Xacid_splash_w:
8306 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8307 boolean has_crumbled_graphics,
8308 int crumbled, int sync_frame)
8310 // if element can be crumbled, but certain action graphics are just empty
8311 // space (like instantly snapping sand to empty space in 1 frame), do not
8312 // treat these empty space graphics as crumbled graphics in EMC engine
8313 if (crumbled == IMG_EMPTY_SPACE)
8314 has_crumbled_graphics = FALSE;
8316 if (has_crumbled_graphics)
8318 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8319 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8320 g_crumbled->anim_delay,
8321 g_crumbled->anim_mode,
8322 g_crumbled->anim_start_frame,
8325 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8326 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8328 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8329 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8331 g_em->has_crumbled_graphics = TRUE;
8335 g_em->crumbled_bitmap = NULL;
8336 g_em->crumbled_src_x = 0;
8337 g_em->crumbled_src_y = 0;
8338 g_em->crumbled_border_size = 0;
8339 g_em->crumbled_tile_size = 0;
8341 g_em->has_crumbled_graphics = FALSE;
8346 void ResetGfxAnimation_EM(int x, int y, int tile)
8352 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8353 int tile, int frame_em, int x, int y)
8355 int action = object_mapping[tile].action;
8356 int direction = object_mapping[tile].direction;
8357 int effective_element = get_effective_element_EM(tile, frame_em);
8358 int graphic = (direction == MV_NONE ?
8359 el_act2img(effective_element, action) :
8360 el_act_dir2img(effective_element, action, direction));
8361 struct GraphicInfo *g = &graphic_info[graphic];
8363 boolean action_removing = (action == ACTION_DIGGING ||
8364 action == ACTION_SNAPPING ||
8365 action == ACTION_COLLECTING);
8366 boolean action_moving = (action == ACTION_FALLING ||
8367 action == ACTION_MOVING ||
8368 action == ACTION_PUSHING ||
8369 action == ACTION_EATING ||
8370 action == ACTION_FILLING ||
8371 action == ACTION_EMPTYING);
8372 boolean action_falling = (action == ACTION_FALLING ||
8373 action == ACTION_FILLING ||
8374 action == ACTION_EMPTYING);
8376 // special case: graphic uses "2nd movement tile" and has defined
8377 // 7 frames for movement animation (or less) => use default graphic
8378 // for last (8th) frame which ends the movement animation
8379 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8381 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8382 graphic = (direction == MV_NONE ?
8383 el_act2img(effective_element, action) :
8384 el_act_dir2img(effective_element, action, direction));
8386 g = &graphic_info[graphic];
8389 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8393 else if (action_moving)
8395 boolean is_backside = object_mapping[tile].is_backside;
8399 int direction = object_mapping[tile].direction;
8400 int move_dir = (action_falling ? MV_DOWN : direction);
8405 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8406 if (g->double_movement && frame_em == 0)
8410 if (move_dir == MV_LEFT)
8411 GfxFrame[x - 1][y] = GfxFrame[x][y];
8412 else if (move_dir == MV_RIGHT)
8413 GfxFrame[x + 1][y] = GfxFrame[x][y];
8414 else if (move_dir == MV_UP)
8415 GfxFrame[x][y - 1] = GfxFrame[x][y];
8416 else if (move_dir == MV_DOWN)
8417 GfxFrame[x][y + 1] = GfxFrame[x][y];
8424 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8425 if (tile == Xsand_stonesand_quickout_1 ||
8426 tile == Xsand_stonesand_quickout_2)
8430 if (graphic_info[graphic].anim_global_sync)
8431 sync_frame = FrameCounter;
8432 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8433 sync_frame = GfxFrame[x][y];
8435 sync_frame = 0; // playfield border (pseudo steel)
8437 SetRandomAnimationValue(x, y);
8439 int frame = getAnimationFrame(g->anim_frames,
8442 g->anim_start_frame,
8445 g_em->unique_identifier =
8446 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8449 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8450 int tile, int frame_em, int x, int y)
8452 int action = object_mapping[tile].action;
8453 int direction = object_mapping[tile].direction;
8454 boolean is_backside = object_mapping[tile].is_backside;
8455 int effective_element = get_effective_element_EM(tile, frame_em);
8456 int effective_action = action;
8457 int graphic = (direction == MV_NONE ?
8458 el_act2img(effective_element, effective_action) :
8459 el_act_dir2img(effective_element, effective_action,
8461 int crumbled = (direction == MV_NONE ?
8462 el_act2crm(effective_element, effective_action) :
8463 el_act_dir2crm(effective_element, effective_action,
8465 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8466 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8467 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8468 struct GraphicInfo *g = &graphic_info[graphic];
8471 // special case: graphic uses "2nd movement tile" and has defined
8472 // 7 frames for movement animation (or less) => use default graphic
8473 // for last (8th) frame which ends the movement animation
8474 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8476 effective_action = ACTION_DEFAULT;
8477 graphic = (direction == MV_NONE ?
8478 el_act2img(effective_element, effective_action) :
8479 el_act_dir2img(effective_element, effective_action,
8481 crumbled = (direction == MV_NONE ?
8482 el_act2crm(effective_element, effective_action) :
8483 el_act_dir2crm(effective_element, effective_action,
8486 g = &graphic_info[graphic];
8489 if (graphic_info[graphic].anim_global_sync)
8490 sync_frame = FrameCounter;
8491 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8492 sync_frame = GfxFrame[x][y];
8494 sync_frame = 0; // playfield border (pseudo steel)
8496 SetRandomAnimationValue(x, y);
8498 int frame = getAnimationFrame(g->anim_frames,
8501 g->anim_start_frame,
8504 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8505 g->double_movement && is_backside);
8507 // (updating the "crumbled" graphic definitions is probably not really needed,
8508 // as animations for crumbled graphics can't be longer than one EMC cycle)
8509 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8513 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8514 int player_nr, int anim, int frame_em)
8516 int element = player_mapping[player_nr][anim].element_rnd;
8517 int action = player_mapping[player_nr][anim].action;
8518 int direction = player_mapping[player_nr][anim].direction;
8519 int graphic = (direction == MV_NONE ?
8520 el_act2img(element, action) :
8521 el_act_dir2img(element, action, direction));
8522 struct GraphicInfo *g = &graphic_info[graphic];
8525 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8527 stored_player[player_nr].StepFrame = frame_em;
8529 sync_frame = stored_player[player_nr].Frame;
8531 int frame = getAnimationFrame(g->anim_frames,
8534 g->anim_start_frame,
8537 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8538 &g_em->src_x, &g_em->src_y, FALSE);
8541 void InitGraphicInfo_EM(void)
8546 int num_em_gfx_errors = 0;
8548 if (graphic_info_em_object[0][0].bitmap == NULL)
8550 // EM graphics not yet initialized in em_open_all()
8555 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8558 // always start with reliable default values
8559 for (i = 0; i < TILE_MAX; i++)
8561 object_mapping[i].element_rnd = EL_UNKNOWN;
8562 object_mapping[i].is_backside = FALSE;
8563 object_mapping[i].action = ACTION_DEFAULT;
8564 object_mapping[i].direction = MV_NONE;
8567 // always start with reliable default values
8568 for (p = 0; p < MAX_PLAYERS; p++)
8570 for (i = 0; i < PLY_MAX; i++)
8572 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8573 player_mapping[p][i].action = ACTION_DEFAULT;
8574 player_mapping[p][i].direction = MV_NONE;
8578 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8580 int e = em_object_mapping_list[i].element_em;
8582 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8583 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8585 if (em_object_mapping_list[i].action != -1)
8586 object_mapping[e].action = em_object_mapping_list[i].action;
8588 if (em_object_mapping_list[i].direction != -1)
8589 object_mapping[e].direction =
8590 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8593 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8595 int a = em_player_mapping_list[i].action_em;
8596 int p = em_player_mapping_list[i].player_nr;
8598 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8600 if (em_player_mapping_list[i].action != -1)
8601 player_mapping[p][a].action = em_player_mapping_list[i].action;
8603 if (em_player_mapping_list[i].direction != -1)
8604 player_mapping[p][a].direction =
8605 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8608 for (i = 0; i < TILE_MAX; i++)
8610 int element = object_mapping[i].element_rnd;
8611 int action = object_mapping[i].action;
8612 int direction = object_mapping[i].direction;
8613 boolean is_backside = object_mapping[i].is_backside;
8614 boolean action_exploding = ((action == ACTION_EXPLODING ||
8615 action == ACTION_SMASHED_BY_ROCK ||
8616 action == ACTION_SMASHED_BY_SPRING) &&
8617 element != EL_DIAMOND);
8618 boolean action_active = (action == ACTION_ACTIVE);
8619 boolean action_other = (action == ACTION_OTHER);
8621 for (j = 0; j < 8; j++)
8623 int effective_element = get_effective_element_EM(i, j);
8624 int effective_action = (j < 7 ? action :
8625 i == Xdrip_stretch ? action :
8626 i == Xdrip_stretchB ? action :
8627 i == Ydrip_1_s ? action :
8628 i == Ydrip_1_sB ? action :
8629 i == Yball_1 ? action :
8630 i == Xball_2 ? action :
8631 i == Yball_2 ? action :
8632 i == Yball_blank ? action :
8633 i == Ykey_1_blank ? action :
8634 i == Ykey_2_blank ? action :
8635 i == Ykey_3_blank ? action :
8636 i == Ykey_4_blank ? action :
8637 i == Ykey_5_blank ? action :
8638 i == Ykey_6_blank ? action :
8639 i == Ykey_7_blank ? action :
8640 i == Ykey_8_blank ? action :
8641 i == Ylenses_blank ? action :
8642 i == Ymagnify_blank ? action :
8643 i == Ygrass_blank ? action :
8644 i == Ydirt_blank ? action :
8645 i == Xsand_stonein_1 ? action :
8646 i == Xsand_stonein_2 ? action :
8647 i == Xsand_stonein_3 ? action :
8648 i == Xsand_stonein_4 ? action :
8649 i == Xsand_stoneout_1 ? action :
8650 i == Xsand_stoneout_2 ? action :
8651 i == Xboom_android ? ACTION_EXPLODING :
8652 action_exploding ? ACTION_EXPLODING :
8653 action_active ? action :
8654 action_other ? action :
8656 int graphic = (el_act_dir2img(effective_element, effective_action,
8658 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8660 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8661 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8662 boolean has_action_graphics = (graphic != base_graphic);
8663 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8664 struct GraphicInfo *g = &graphic_info[graphic];
8665 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8668 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8669 boolean special_animation = (action != ACTION_DEFAULT &&
8670 g->anim_frames == 3 &&
8671 g->anim_delay == 2 &&
8672 g->anim_mode & ANIM_LINEAR);
8673 int sync_frame = (i == Xdrip_stretch ? 7 :
8674 i == Xdrip_stretchB ? 7 :
8675 i == Ydrip_2_s ? j + 8 :
8676 i == Ydrip_2_sB ? j + 8 :
8685 i == Xfake_acid_1 ? 0 :
8686 i == Xfake_acid_2 ? 10 :
8687 i == Xfake_acid_3 ? 20 :
8688 i == Xfake_acid_4 ? 30 :
8689 i == Xfake_acid_5 ? 40 :
8690 i == Xfake_acid_6 ? 50 :
8691 i == Xfake_acid_7 ? 60 :
8692 i == Xfake_acid_8 ? 70 :
8694 i == Yball_2 ? j + 8 :
8695 i == Yball_blank ? j + 1 :
8696 i == Ykey_1_blank ? j + 1 :
8697 i == Ykey_2_blank ? j + 1 :
8698 i == Ykey_3_blank ? j + 1 :
8699 i == Ykey_4_blank ? j + 1 :
8700 i == Ykey_5_blank ? j + 1 :
8701 i == Ykey_6_blank ? j + 1 :
8702 i == Ykey_7_blank ? j + 1 :
8703 i == Ykey_8_blank ? j + 1 :
8704 i == Ylenses_blank ? j + 1 :
8705 i == Ymagnify_blank ? j + 1 :
8706 i == Ygrass_blank ? j + 1 :
8707 i == Ydirt_blank ? j + 1 :
8708 i == Xamoeba_1 ? 0 :
8709 i == Xamoeba_2 ? 1 :
8710 i == Xamoeba_3 ? 2 :
8711 i == Xamoeba_4 ? 3 :
8712 i == Xamoeba_5 ? 0 :
8713 i == Xamoeba_6 ? 1 :
8714 i == Xamoeba_7 ? 2 :
8715 i == Xamoeba_8 ? 3 :
8716 i == Xexit_2 ? j + 8 :
8717 i == Xexit_3 ? j + 16 :
8718 i == Xdynamite_1 ? 0 :
8719 i == Xdynamite_2 ? 8 :
8720 i == Xdynamite_3 ? 16 :
8721 i == Xdynamite_4 ? 24 :
8722 i == Xsand_stonein_1 ? j + 1 :
8723 i == Xsand_stonein_2 ? j + 9 :
8724 i == Xsand_stonein_3 ? j + 17 :
8725 i == Xsand_stonein_4 ? j + 25 :
8726 i == Xsand_stoneout_1 && j == 0 ? 0 :
8727 i == Xsand_stoneout_1 && j == 1 ? 0 :
8728 i == Xsand_stoneout_1 && j == 2 ? 1 :
8729 i == Xsand_stoneout_1 && j == 3 ? 2 :
8730 i == Xsand_stoneout_1 && j == 4 ? 2 :
8731 i == Xsand_stoneout_1 && j == 5 ? 3 :
8732 i == Xsand_stoneout_1 && j == 6 ? 4 :
8733 i == Xsand_stoneout_1 && j == 7 ? 4 :
8734 i == Xsand_stoneout_2 && j == 0 ? 5 :
8735 i == Xsand_stoneout_2 && j == 1 ? 6 :
8736 i == Xsand_stoneout_2 && j == 2 ? 7 :
8737 i == Xsand_stoneout_2 && j == 3 ? 8 :
8738 i == Xsand_stoneout_2 && j == 4 ? 9 :
8739 i == Xsand_stoneout_2 && j == 5 ? 11 :
8740 i == Xsand_stoneout_2 && j == 6 ? 13 :
8741 i == Xsand_stoneout_2 && j == 7 ? 15 :
8742 i == Xboom_bug && j == 1 ? 2 :
8743 i == Xboom_bug && j == 2 ? 2 :
8744 i == Xboom_bug && j == 3 ? 4 :
8745 i == Xboom_bug && j == 4 ? 4 :
8746 i == Xboom_bug && j == 5 ? 2 :
8747 i == Xboom_bug && j == 6 ? 2 :
8748 i == Xboom_bug && j == 7 ? 0 :
8749 i == Xboom_bomb && j == 1 ? 2 :
8750 i == Xboom_bomb && j == 2 ? 2 :
8751 i == Xboom_bomb && j == 3 ? 4 :
8752 i == Xboom_bomb && j == 4 ? 4 :
8753 i == Xboom_bomb && j == 5 ? 2 :
8754 i == Xboom_bomb && j == 6 ? 2 :
8755 i == Xboom_bomb && j == 7 ? 0 :
8756 i == Xboom_android && j == 7 ? 6 :
8757 i == Xboom_1 && j == 1 ? 2 :
8758 i == Xboom_1 && j == 2 ? 2 :
8759 i == Xboom_1 && j == 3 ? 4 :
8760 i == Xboom_1 && j == 4 ? 4 :
8761 i == Xboom_1 && j == 5 ? 6 :
8762 i == Xboom_1 && j == 6 ? 6 :
8763 i == Xboom_1 && j == 7 ? 8 :
8764 i == Xboom_2 && j == 0 ? 8 :
8765 i == Xboom_2 && j == 1 ? 8 :
8766 i == Xboom_2 && j == 2 ? 10 :
8767 i == Xboom_2 && j == 3 ? 10 :
8768 i == Xboom_2 && j == 4 ? 10 :
8769 i == Xboom_2 && j == 5 ? 12 :
8770 i == Xboom_2 && j == 6 ? 12 :
8771 i == Xboom_2 && j == 7 ? 12 :
8772 special_animation && j == 4 ? 3 :
8773 effective_action != action ? 0 :
8777 Bitmap *debug_bitmap = g_em->bitmap;
8778 int debug_src_x = g_em->src_x;
8779 int debug_src_y = g_em->src_y;
8782 int frame = getAnimationFrame(g->anim_frames,
8785 g->anim_start_frame,
8788 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8789 g->double_movement && is_backside);
8791 g_em->bitmap = src_bitmap;
8792 g_em->src_x = src_x;
8793 g_em->src_y = src_y;
8794 g_em->src_offset_x = 0;
8795 g_em->src_offset_y = 0;
8796 g_em->dst_offset_x = 0;
8797 g_em->dst_offset_y = 0;
8798 g_em->width = TILEX;
8799 g_em->height = TILEY;
8801 g_em->preserve_background = FALSE;
8803 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8806 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8807 effective_action == ACTION_MOVING ||
8808 effective_action == ACTION_PUSHING ||
8809 effective_action == ACTION_EATING)) ||
8810 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8811 effective_action == ACTION_EMPTYING)))
8814 (effective_action == ACTION_FALLING ||
8815 effective_action == ACTION_FILLING ||
8816 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8817 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8818 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8819 int num_steps = (i == Ydrip_1_s ? 16 :
8820 i == Ydrip_1_sB ? 16 :
8821 i == Ydrip_2_s ? 16 :
8822 i == Ydrip_2_sB ? 16 :
8823 i == Xsand_stonein_1 ? 32 :
8824 i == Xsand_stonein_2 ? 32 :
8825 i == Xsand_stonein_3 ? 32 :
8826 i == Xsand_stonein_4 ? 32 :
8827 i == Xsand_stoneout_1 ? 16 :
8828 i == Xsand_stoneout_2 ? 16 : 8);
8829 int cx = ABS(dx) * (TILEX / num_steps);
8830 int cy = ABS(dy) * (TILEY / num_steps);
8831 int step_frame = (i == Ydrip_2_s ? j + 8 :
8832 i == Ydrip_2_sB ? j + 8 :
8833 i == Xsand_stonein_2 ? j + 8 :
8834 i == Xsand_stonein_3 ? j + 16 :
8835 i == Xsand_stonein_4 ? j + 24 :
8836 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8837 int step = (is_backside ? step_frame : num_steps - step_frame);
8839 if (is_backside) // tile where movement starts
8841 if (dx < 0 || dy < 0)
8843 g_em->src_offset_x = cx * step;
8844 g_em->src_offset_y = cy * step;
8848 g_em->dst_offset_x = cx * step;
8849 g_em->dst_offset_y = cy * step;
8852 else // tile where movement ends
8854 if (dx < 0 || dy < 0)
8856 g_em->dst_offset_x = cx * step;
8857 g_em->dst_offset_y = cy * step;
8861 g_em->src_offset_x = cx * step;
8862 g_em->src_offset_y = cy * step;
8866 g_em->width = TILEX - cx * step;
8867 g_em->height = TILEY - cy * step;
8870 // create unique graphic identifier to decide if tile must be redrawn
8871 /* bit 31 - 16 (16 bit): EM style graphic
8872 bit 15 - 12 ( 4 bit): EM style frame
8873 bit 11 - 6 ( 6 bit): graphic width
8874 bit 5 - 0 ( 6 bit): graphic height */
8875 g_em->unique_identifier =
8876 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8880 // skip check for EMC elements not contained in original EMC artwork
8881 if (element == EL_EMC_FAKE_ACID)
8884 if (g_em->bitmap != debug_bitmap ||
8885 g_em->src_x != debug_src_x ||
8886 g_em->src_y != debug_src_y ||
8887 g_em->src_offset_x != 0 ||
8888 g_em->src_offset_y != 0 ||
8889 g_em->dst_offset_x != 0 ||
8890 g_em->dst_offset_y != 0 ||
8891 g_em->width != TILEX ||
8892 g_em->height != TILEY)
8894 static int last_i = -1;
8902 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8903 i, element, element_info[element].token_name,
8904 element_action_info[effective_action].suffix, direction);
8906 if (element != effective_element)
8907 printf(" [%d ('%s')]",
8909 element_info[effective_element].token_name);
8913 if (g_em->bitmap != debug_bitmap)
8914 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8915 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8917 if (g_em->src_x != debug_src_x ||
8918 g_em->src_y != debug_src_y)
8919 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8920 j, (is_backside ? 'B' : 'F'),
8921 g_em->src_x, g_em->src_y,
8922 g_em->src_x / 32, g_em->src_y / 32,
8923 debug_src_x, debug_src_y,
8924 debug_src_x / 32, debug_src_y / 32);
8926 if (g_em->src_offset_x != 0 ||
8927 g_em->src_offset_y != 0 ||
8928 g_em->dst_offset_x != 0 ||
8929 g_em->dst_offset_y != 0)
8930 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8932 g_em->src_offset_x, g_em->src_offset_y,
8933 g_em->dst_offset_x, g_em->dst_offset_y);
8935 if (g_em->width != TILEX ||
8936 g_em->height != TILEY)
8937 printf(" %d (%d): size %d,%d should be %d,%d\n",
8939 g_em->width, g_em->height, TILEX, TILEY);
8941 num_em_gfx_errors++;
8948 for (i = 0; i < TILE_MAX; i++)
8950 for (j = 0; j < 8; j++)
8952 int element = object_mapping[i].element_rnd;
8953 int action = object_mapping[i].action;
8954 int direction = object_mapping[i].direction;
8955 boolean is_backside = object_mapping[i].is_backside;
8956 int graphic_action = el_act_dir2img(element, action, direction);
8957 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8959 if ((action == ACTION_SMASHED_BY_ROCK ||
8960 action == ACTION_SMASHED_BY_SPRING ||
8961 action == ACTION_EATING) &&
8962 graphic_action == graphic_default)
8964 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8965 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8966 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8967 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8970 // no separate animation for "smashed by rock" -- use rock instead
8971 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8972 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8974 g_em->bitmap = g_xx->bitmap;
8975 g_em->src_x = g_xx->src_x;
8976 g_em->src_y = g_xx->src_y;
8977 g_em->src_offset_x = g_xx->src_offset_x;
8978 g_em->src_offset_y = g_xx->src_offset_y;
8979 g_em->dst_offset_x = g_xx->dst_offset_x;
8980 g_em->dst_offset_y = g_xx->dst_offset_y;
8981 g_em->width = g_xx->width;
8982 g_em->height = g_xx->height;
8983 g_em->unique_identifier = g_xx->unique_identifier;
8986 g_em->preserve_background = TRUE;
8991 for (p = 0; p < MAX_PLAYERS; p++)
8993 for (i = 0; i < PLY_MAX; i++)
8995 int element = player_mapping[p][i].element_rnd;
8996 int action = player_mapping[p][i].action;
8997 int direction = player_mapping[p][i].direction;
8999 for (j = 0; j < 8; j++)
9001 int effective_element = element;
9002 int effective_action = action;
9003 int graphic = (direction == MV_NONE ?
9004 el_act2img(effective_element, effective_action) :
9005 el_act_dir2img(effective_element, effective_action,
9007 struct GraphicInfo *g = &graphic_info[graphic];
9008 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
9014 Bitmap *debug_bitmap = g_em->bitmap;
9015 int debug_src_x = g_em->src_x;
9016 int debug_src_y = g_em->src_y;
9019 int frame = getAnimationFrame(g->anim_frames,
9022 g->anim_start_frame,
9025 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9027 g_em->bitmap = src_bitmap;
9028 g_em->src_x = src_x;
9029 g_em->src_y = src_y;
9030 g_em->src_offset_x = 0;
9031 g_em->src_offset_y = 0;
9032 g_em->dst_offset_x = 0;
9033 g_em->dst_offset_y = 0;
9034 g_em->width = TILEX;
9035 g_em->height = TILEY;
9039 // skip check for EMC elements not contained in original EMC artwork
9040 if (element == EL_PLAYER_3 ||
9041 element == EL_PLAYER_4)
9044 if (g_em->bitmap != debug_bitmap ||
9045 g_em->src_x != debug_src_x ||
9046 g_em->src_y != debug_src_y)
9048 static int last_i = -1;
9056 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9057 p, i, element, element_info[element].token_name,
9058 element_action_info[effective_action].suffix, direction);
9060 if (element != effective_element)
9061 printf(" [%d ('%s')]",
9063 element_info[effective_element].token_name);
9067 if (g_em->bitmap != debug_bitmap)
9068 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9069 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9071 if (g_em->src_x != debug_src_x ||
9072 g_em->src_y != debug_src_y)
9073 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9075 g_em->src_x, g_em->src_y,
9076 g_em->src_x / 32, g_em->src_y / 32,
9077 debug_src_x, debug_src_y,
9078 debug_src_x / 32, debug_src_y / 32);
9080 num_em_gfx_errors++;
9090 printf("::: [%d errors found]\n", num_em_gfx_errors);
9096 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9097 boolean any_player_moving,
9098 boolean any_player_snapping,
9099 boolean any_player_dropping)
9101 if (frame == 0 && !any_player_dropping)
9103 if (!local_player->was_waiting)
9105 if (!CheckSaveEngineSnapshotToList())
9108 local_player->was_waiting = TRUE;
9111 else if (any_player_moving || any_player_snapping || any_player_dropping)
9113 local_player->was_waiting = FALSE;
9117 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9118 boolean murphy_is_dropping)
9120 if (murphy_is_waiting)
9122 if (!local_player->was_waiting)
9124 if (!CheckSaveEngineSnapshotToList())
9127 local_player->was_waiting = TRUE;
9132 local_player->was_waiting = FALSE;
9136 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9137 boolean button_released)
9139 if (button_released)
9141 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9142 CheckSaveEngineSnapshotToList();
9144 else if (element_clicked)
9146 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9147 CheckSaveEngineSnapshotToList();
9149 game.snapshot.changed_action = TRUE;
9153 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9154 boolean any_player_moving,
9155 boolean any_player_snapping,
9156 boolean any_player_dropping)
9158 if (tape.single_step && tape.recording && !tape.pausing)
9159 if (frame == 0 && !any_player_dropping)
9160 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9162 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9163 any_player_snapping, any_player_dropping);
9166 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9167 boolean murphy_is_dropping)
9169 boolean murphy_starts_dropping = FALSE;
9172 for (i = 0; i < MAX_PLAYERS; i++)
9173 if (stored_player[i].force_dropping)
9174 murphy_starts_dropping = TRUE;
9176 if (tape.single_step && tape.recording && !tape.pausing)
9177 if (murphy_is_waiting && !murphy_starts_dropping)
9178 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9180 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9183 void CheckSingleStepMode_MM(boolean element_clicked,
9184 boolean button_released)
9186 if (tape.single_step && tape.recording && !tape.pausing)
9187 if (button_released)
9188 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9190 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9193 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9194 int graphic, int sync_frame, int x, int y)
9196 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9198 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9201 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9203 return (IS_NEXT_FRAME(sync_frame, graphic));
9206 int getGraphicInfo_Delay(int graphic)
9208 return graphic_info[graphic].anim_delay;
9211 void PlayMenuSoundExt(int sound)
9213 if (sound == SND_UNDEFINED)
9216 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9217 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9220 if (IS_LOOP_SOUND(sound))
9221 PlaySoundLoop(sound);
9226 void PlayMenuSound(void)
9228 PlayMenuSoundExt(menu.sound[game_status]);
9231 void PlayMenuSoundStereo(int sound, int stereo_position)
9233 if (sound == SND_UNDEFINED)
9236 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9237 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9240 if (IS_LOOP_SOUND(sound))
9241 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9243 PlaySoundStereo(sound, stereo_position);
9246 void PlayMenuSoundIfLoopExt(int sound)
9248 if (sound == SND_UNDEFINED)
9251 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9252 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9255 if (IS_LOOP_SOUND(sound))
9256 PlaySoundLoop(sound);
9259 void PlayMenuSoundIfLoop(void)
9261 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9264 void PlayMenuMusicExt(int music)
9266 if (music == MUS_UNDEFINED)
9269 if (!setup.sound_music)
9272 if (IS_LOOP_MUSIC(music))
9273 PlayMusicLoop(music);
9278 void PlayMenuMusic(void)
9280 char *curr_music = getCurrentlyPlayingMusicFilename();
9281 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9283 if (!strEqual(curr_music, next_music))
9284 PlayMenuMusicExt(menu.music[game_status]);
9287 void PlayMenuSoundsAndMusic(void)
9293 static void FadeMenuSounds(void)
9298 static void FadeMenuMusic(void)
9300 char *curr_music = getCurrentlyPlayingMusicFilename();
9301 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9303 if (!strEqual(curr_music, next_music))
9307 void FadeMenuSoundsAndMusic(void)
9313 void PlaySoundActivating(void)
9316 PlaySound(SND_MENU_ITEM_ACTIVATING);
9320 void PlaySoundSelecting(void)
9323 PlaySound(SND_MENU_ITEM_SELECTING);
9327 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9329 boolean change_fullscreen = (setup.fullscreen !=
9330 video.fullscreen_enabled);
9331 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9332 setup.window_scaling_percent !=
9333 video.window_scaling_percent);
9335 if (change_window_scaling_percent && video.fullscreen_enabled)
9338 if (!change_window_scaling_percent && !video.fullscreen_available)
9341 if (change_window_scaling_percent)
9343 SDLSetWindowScaling(setup.window_scaling_percent);
9347 else if (change_fullscreen)
9349 SDLSetWindowFullscreen(setup.fullscreen);
9351 // set setup value according to successfully changed fullscreen mode
9352 setup.fullscreen = video.fullscreen_enabled;
9357 if (change_fullscreen ||
9358 change_window_scaling_percent)
9360 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9362 // save backbuffer content which gets lost when toggling fullscreen mode
9363 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9365 if (change_window_scaling_percent)
9367 // keep window mode, but change window scaling
9368 video.fullscreen_enabled = TRUE; // force new window scaling
9371 // toggle fullscreen
9372 ChangeVideoModeIfNeeded(setup.fullscreen);
9374 // set setup value according to successfully changed fullscreen mode
9375 setup.fullscreen = video.fullscreen_enabled;
9377 // restore backbuffer content from temporary backbuffer backup bitmap
9378 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9380 FreeBitmap(tmp_backbuffer);
9382 // update visible window/screen
9383 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9387 static void JoinRectangles(int *x, int *y, int *width, int *height,
9388 int x2, int y2, int width2, int height2)
9390 // do not join with "off-screen" rectangle
9391 if (x2 == -1 || y2 == -1)
9396 *width = MAX(*width, width2);
9397 *height = MAX(*height, height2);
9400 void SetAnimStatus(int anim_status_new)
9402 if (anim_status_new == GAME_MODE_MAIN)
9403 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9404 else if (anim_status_new == GAME_MODE_SCORES)
9405 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9407 global.anim_status_next = anim_status_new;
9409 // directly set screen modes that are entered without fading
9410 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9411 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9412 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9413 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9414 global.anim_status = global.anim_status_next;
9417 void SetGameStatus(int game_status_new)
9419 if (game_status_new != game_status)
9420 game_status_last_screen = game_status;
9422 game_status = game_status_new;
9424 SetAnimStatus(game_status_new);
9427 void SetFontStatus(int game_status_new)
9429 static int last_game_status = -1;
9431 if (game_status_new != -1)
9433 // set game status for font use after storing last game status
9434 last_game_status = game_status;
9435 game_status = game_status_new;
9439 // reset game status after font use from last stored game status
9440 game_status = last_game_status;
9444 void ResetFontStatus(void)
9449 void SetLevelSetInfo(char *identifier, int level_nr)
9451 setString(&levelset.identifier, identifier);
9453 levelset.level_nr = level_nr;
9456 boolean CheckIfAllViewportsHaveChanged(void)
9458 // if game status has not changed, viewports have not changed either
9459 if (game_status == game_status_last)
9462 // check if all viewports have changed with current game status
9464 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9465 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9466 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9467 int new_real_sx = vp_playfield->x;
9468 int new_real_sy = vp_playfield->y;
9469 int new_full_sxsize = vp_playfield->width;
9470 int new_full_sysize = vp_playfield->height;
9471 int new_dx = vp_door_1->x;
9472 int new_dy = vp_door_1->y;
9473 int new_dxsize = vp_door_1->width;
9474 int new_dysize = vp_door_1->height;
9475 int new_vx = vp_door_2->x;
9476 int new_vy = vp_door_2->y;
9477 int new_vxsize = vp_door_2->width;
9478 int new_vysize = vp_door_2->height;
9480 boolean playfield_viewport_has_changed =
9481 (new_real_sx != REAL_SX ||
9482 new_real_sy != REAL_SY ||
9483 new_full_sxsize != FULL_SXSIZE ||
9484 new_full_sysize != FULL_SYSIZE);
9486 boolean door_1_viewport_has_changed =
9489 new_dxsize != DXSIZE ||
9490 new_dysize != DYSIZE);
9492 boolean door_2_viewport_has_changed =
9495 new_vxsize != VXSIZE ||
9496 new_vysize != VYSIZE ||
9497 game_status_last == GAME_MODE_EDITOR);
9499 return (playfield_viewport_has_changed &&
9500 door_1_viewport_has_changed &&
9501 door_2_viewport_has_changed);
9504 boolean CheckFadeAll(void)
9506 return (CheckIfGlobalBorderHasChanged() ||
9507 CheckIfAllViewportsHaveChanged());
9510 void ChangeViewportPropertiesIfNeeded(void)
9512 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9513 FALSE : setup.small_game_graphics);
9514 int gfx_game_mode = game_status;
9515 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9517 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9518 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9519 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9520 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9521 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9522 int new_win_xsize = vp_window->width;
9523 int new_win_ysize = vp_window->height;
9524 int border_left = vp_playfield->border_left;
9525 int border_right = vp_playfield->border_right;
9526 int border_top = vp_playfield->border_top;
9527 int border_bottom = vp_playfield->border_bottom;
9528 int new_sx = vp_playfield->x + border_left;
9529 int new_sy = vp_playfield->y + border_top;
9530 int new_sxsize = vp_playfield->width - border_left - border_right;
9531 int new_sysize = vp_playfield->height - border_top - border_bottom;
9532 int new_real_sx = vp_playfield->x;
9533 int new_real_sy = vp_playfield->y;
9534 int new_full_sxsize = vp_playfield->width;
9535 int new_full_sysize = vp_playfield->height;
9536 int new_dx = vp_door_1->x;
9537 int new_dy = vp_door_1->y;
9538 int new_dxsize = vp_door_1->width;
9539 int new_dysize = vp_door_1->height;
9540 int new_vx = vp_door_2->x;
9541 int new_vy = vp_door_2->y;
9542 int new_vxsize = vp_door_2->width;
9543 int new_vysize = vp_door_2->height;
9544 int new_ex = vp_door_3->x;
9545 int new_ey = vp_door_3->y;
9546 int new_exsize = vp_door_3->width;
9547 int new_eysize = vp_door_3->height;
9548 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9549 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9550 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9551 int new_scr_fieldx = new_sxsize / tilesize;
9552 int new_scr_fieldy = new_sysize / tilesize;
9553 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9554 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9555 boolean init_gfx_buffers = FALSE;
9556 boolean init_video_buffer = FALSE;
9557 boolean init_gadgets_and_anims = FALSE;
9558 boolean init_em_graphics = FALSE;
9560 if (new_win_xsize != WIN_XSIZE ||
9561 new_win_ysize != WIN_YSIZE)
9563 WIN_XSIZE = new_win_xsize;
9564 WIN_YSIZE = new_win_ysize;
9566 init_video_buffer = TRUE;
9567 init_gfx_buffers = TRUE;
9568 init_gadgets_and_anims = TRUE;
9570 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9573 if (new_scr_fieldx != SCR_FIELDX ||
9574 new_scr_fieldy != SCR_FIELDY)
9576 // this always toggles between MAIN and GAME when using small tile size
9578 SCR_FIELDX = new_scr_fieldx;
9579 SCR_FIELDY = new_scr_fieldy;
9581 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9592 new_sxsize != SXSIZE ||
9593 new_sysize != SYSIZE ||
9594 new_dxsize != DXSIZE ||
9595 new_dysize != DYSIZE ||
9596 new_vxsize != VXSIZE ||
9597 new_vysize != VYSIZE ||
9598 new_exsize != EXSIZE ||
9599 new_eysize != EYSIZE ||
9600 new_real_sx != REAL_SX ||
9601 new_real_sy != REAL_SY ||
9602 new_full_sxsize != FULL_SXSIZE ||
9603 new_full_sysize != FULL_SYSIZE ||
9604 new_tilesize_var != TILESIZE_VAR
9607 // ------------------------------------------------------------------------
9608 // determine next fading area for changed viewport definitions
9609 // ------------------------------------------------------------------------
9611 // start with current playfield area (default fading area)
9614 FADE_SXSIZE = FULL_SXSIZE;
9615 FADE_SYSIZE = FULL_SYSIZE;
9617 // add new playfield area if position or size has changed
9618 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9619 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9621 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9622 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9625 // add current and new door 1 area if position or size has changed
9626 if (new_dx != DX || new_dy != DY ||
9627 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9629 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9630 DX, DY, DXSIZE, DYSIZE);
9631 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9632 new_dx, new_dy, new_dxsize, new_dysize);
9635 // add current and new door 2 area if position or size has changed
9636 if (new_vx != VX || new_vy != VY ||
9637 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9639 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9640 VX, VY, VXSIZE, VYSIZE);
9641 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9642 new_vx, new_vy, new_vxsize, new_vysize);
9645 // ------------------------------------------------------------------------
9646 // handle changed tile size
9647 // ------------------------------------------------------------------------
9649 if (new_tilesize_var != TILESIZE_VAR)
9651 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9653 // changing tile size invalidates scroll values of engine snapshots
9654 FreeEngineSnapshotSingle();
9656 // changing tile size requires update of graphic mapping for EM engine
9657 init_em_graphics = TRUE;
9668 SXSIZE = new_sxsize;
9669 SYSIZE = new_sysize;
9670 DXSIZE = new_dxsize;
9671 DYSIZE = new_dysize;
9672 VXSIZE = new_vxsize;
9673 VYSIZE = new_vysize;
9674 EXSIZE = new_exsize;
9675 EYSIZE = new_eysize;
9676 REAL_SX = new_real_sx;
9677 REAL_SY = new_real_sy;
9678 FULL_SXSIZE = new_full_sxsize;
9679 FULL_SYSIZE = new_full_sysize;
9680 TILESIZE_VAR = new_tilesize_var;
9682 init_gfx_buffers = TRUE;
9683 init_gadgets_and_anims = TRUE;
9685 // printf("::: viewports: init_gfx_buffers\n");
9686 // printf("::: viewports: init_gadgets_and_anims\n");
9689 if (init_gfx_buffers)
9691 // printf("::: init_gfx_buffers\n");
9693 SCR_FIELDX = new_scr_fieldx_buffers;
9694 SCR_FIELDY = new_scr_fieldy_buffers;
9698 SCR_FIELDX = new_scr_fieldx;
9699 SCR_FIELDY = new_scr_fieldy;
9701 SetDrawDeactivationMask(REDRAW_NONE);
9702 SetDrawBackgroundMask(REDRAW_FIELD);
9705 if (init_video_buffer)
9707 // printf("::: init_video_buffer\n");
9709 FreeAllImageTextures(); // needs old renderer to free the textures
9711 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9712 InitImageTextures();
9715 if (init_gadgets_and_anims)
9717 // printf("::: init_gadgets_and_anims\n");
9720 InitGlobalAnimations();
9723 if (init_em_graphics)
9725 InitGraphicInfo_EM();