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 Yacid_splash_eB, FALSE, FALSE,
5749 EL_ACID_SPLASH_RIGHT, -1, -1
5752 Yacid_splash_wB, FALSE, FALSE,
5753 EL_ACID_SPLASH_LEFT, -1, -1
5756 #ifdef EM_ENGINE_BAD_ROLL
5758 Xstone_force_e, FALSE, FALSE,
5759 EL_ROCK, -1, MV_BIT_RIGHT
5762 Xstone_force_w, FALSE, FALSE,
5763 EL_ROCK, -1, MV_BIT_LEFT
5766 Xnut_force_e, FALSE, FALSE,
5767 EL_NUT, -1, MV_BIT_RIGHT
5770 Xnut_force_w, FALSE, FALSE,
5771 EL_NUT, -1, MV_BIT_LEFT
5774 Xspring_force_e, FALSE, FALSE,
5775 EL_SPRING, -1, MV_BIT_RIGHT
5778 Xspring_force_w, FALSE, FALSE,
5779 EL_SPRING, -1, MV_BIT_LEFT
5782 Xemerald_force_e, FALSE, FALSE,
5783 EL_EMERALD, -1, MV_BIT_RIGHT
5786 Xemerald_force_w, FALSE, FALSE,
5787 EL_EMERALD, -1, MV_BIT_LEFT
5790 Xdiamond_force_e, FALSE, FALSE,
5791 EL_DIAMOND, -1, MV_BIT_RIGHT
5794 Xdiamond_force_w, FALSE, FALSE,
5795 EL_DIAMOND, -1, MV_BIT_LEFT
5798 Xbomb_force_e, FALSE, FALSE,
5799 EL_BOMB, -1, MV_BIT_RIGHT
5802 Xbomb_force_w, FALSE, FALSE,
5803 EL_BOMB, -1, MV_BIT_LEFT
5805 #endif // EM_ENGINE_BAD_ROLL
5808 Xstone, TRUE, FALSE,
5812 Xstone_pause, FALSE, FALSE,
5816 Xstone_fall, FALSE, FALSE,
5820 Ystone_s, FALSE, FALSE,
5821 EL_ROCK, ACTION_FALLING, -1
5824 Ystone_sB, FALSE, TRUE,
5825 EL_ROCK, ACTION_FALLING, -1
5828 Ystone_e, FALSE, FALSE,
5829 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5832 Ystone_eB, FALSE, TRUE,
5833 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5836 Ystone_w, FALSE, FALSE,
5837 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5840 Ystone_wB, FALSE, TRUE,
5841 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5848 Xnut_pause, FALSE, FALSE,
5852 Xnut_fall, FALSE, FALSE,
5856 Ynut_s, FALSE, FALSE,
5857 EL_NUT, ACTION_FALLING, -1
5860 Ynut_sB, FALSE, TRUE,
5861 EL_NUT, ACTION_FALLING, -1
5864 Ynut_e, FALSE, FALSE,
5865 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5868 Ynut_eB, FALSE, TRUE,
5869 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5872 Ynut_w, FALSE, FALSE,
5873 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5876 Ynut_wB, FALSE, TRUE,
5877 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5880 Xbug_n, TRUE, FALSE,
5884 Xbug_e, TRUE, FALSE,
5885 EL_BUG_RIGHT, -1, -1
5888 Xbug_s, TRUE, FALSE,
5892 Xbug_w, TRUE, FALSE,
5896 Xbug_gon, FALSE, FALSE,
5900 Xbug_goe, FALSE, FALSE,
5901 EL_BUG_RIGHT, -1, -1
5904 Xbug_gos, FALSE, FALSE,
5908 Xbug_gow, FALSE, FALSE,
5912 Ybug_n, FALSE, FALSE,
5913 EL_BUG, ACTION_MOVING, MV_BIT_UP
5916 Ybug_nB, FALSE, TRUE,
5917 EL_BUG, ACTION_MOVING, MV_BIT_UP
5920 Ybug_e, FALSE, FALSE,
5921 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5924 Ybug_eB, FALSE, TRUE,
5925 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5928 Ybug_s, FALSE, FALSE,
5929 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5932 Ybug_sB, FALSE, TRUE,
5933 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5936 Ybug_w, FALSE, FALSE,
5937 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5940 Ybug_wB, FALSE, TRUE,
5941 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5944 Ybug_w_n, FALSE, FALSE,
5945 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5948 Ybug_n_e, FALSE, FALSE,
5949 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5952 Ybug_e_s, FALSE, FALSE,
5953 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5956 Ybug_s_w, FALSE, FALSE,
5957 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5960 Ybug_e_n, FALSE, FALSE,
5961 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5964 Ybug_s_e, FALSE, FALSE,
5965 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5968 Ybug_w_s, FALSE, FALSE,
5969 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5972 Ybug_n_w, FALSE, FALSE,
5973 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5976 Ybug_stone, FALSE, FALSE,
5977 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5980 Ybug_spring, FALSE, FALSE,
5981 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5984 Xtank_n, TRUE, FALSE,
5985 EL_SPACESHIP_UP, -1, -1
5988 Xtank_e, TRUE, FALSE,
5989 EL_SPACESHIP_RIGHT, -1, -1
5992 Xtank_s, TRUE, FALSE,
5993 EL_SPACESHIP_DOWN, -1, -1
5996 Xtank_w, TRUE, FALSE,
5997 EL_SPACESHIP_LEFT, -1, -1
6000 Xtank_gon, FALSE, FALSE,
6001 EL_SPACESHIP_UP, -1, -1
6004 Xtank_goe, FALSE, FALSE,
6005 EL_SPACESHIP_RIGHT, -1, -1
6008 Xtank_gos, FALSE, FALSE,
6009 EL_SPACESHIP_DOWN, -1, -1
6012 Xtank_gow, FALSE, FALSE,
6013 EL_SPACESHIP_LEFT, -1, -1
6016 Ytank_n, FALSE, FALSE,
6017 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6020 Ytank_nB, FALSE, TRUE,
6021 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6024 Ytank_e, FALSE, FALSE,
6025 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6028 Ytank_eB, FALSE, TRUE,
6029 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6032 Ytank_s, FALSE, FALSE,
6033 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6036 Ytank_sB, FALSE, TRUE,
6037 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6040 Ytank_w, FALSE, FALSE,
6041 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6044 Ytank_wB, FALSE, TRUE,
6045 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6048 Ytank_w_n, FALSE, FALSE,
6049 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6052 Ytank_n_e, FALSE, FALSE,
6053 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6056 Ytank_e_s, FALSE, FALSE,
6057 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6060 Ytank_s_w, FALSE, FALSE,
6061 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6064 Ytank_e_n, FALSE, FALSE,
6065 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6068 Ytank_s_e, FALSE, FALSE,
6069 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6072 Ytank_w_s, FALSE, FALSE,
6073 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6076 Ytank_n_w, FALSE, FALSE,
6077 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6080 Ytank_stone, FALSE, FALSE,
6081 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6084 Ytank_spring, FALSE, FALSE,
6085 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6088 Xandroid, TRUE, FALSE,
6089 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6092 Xandroid_1_n, FALSE, FALSE,
6093 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6096 Xandroid_2_n, FALSE, FALSE,
6097 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6100 Xandroid_1_e, FALSE, FALSE,
6101 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6104 Xandroid_2_e, FALSE, FALSE,
6105 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6108 Xandroid_1_w, FALSE, FALSE,
6109 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6112 Xandroid_2_w, FALSE, FALSE,
6113 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6116 Xandroid_1_s, FALSE, FALSE,
6117 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6120 Xandroid_2_s, FALSE, FALSE,
6121 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6124 Yandroid_n, FALSE, FALSE,
6125 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6128 Yandroid_nB, FALSE, TRUE,
6129 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6132 Yandroid_ne, FALSE, FALSE,
6133 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6136 Yandroid_neB, FALSE, TRUE,
6137 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6140 Yandroid_e, FALSE, FALSE,
6141 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6144 Yandroid_eB, FALSE, TRUE,
6145 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6148 Yandroid_se, FALSE, FALSE,
6149 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6152 Yandroid_seB, FALSE, TRUE,
6153 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6156 Yandroid_s, FALSE, FALSE,
6157 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6160 Yandroid_sB, FALSE, TRUE,
6161 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6164 Yandroid_sw, FALSE, FALSE,
6165 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6168 Yandroid_swB, FALSE, TRUE,
6169 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6172 Yandroid_w, FALSE, FALSE,
6173 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6176 Yandroid_wB, FALSE, TRUE,
6177 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6180 Yandroid_nw, FALSE, FALSE,
6181 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6184 Yandroid_nwB, FALSE, TRUE,
6185 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6188 Xspring, TRUE, FALSE,
6192 Xspring_pause, FALSE, FALSE,
6196 Xspring_e, FALSE, FALSE,
6200 Xspring_w, FALSE, FALSE,
6204 Xspring_fall, FALSE, FALSE,
6208 Yspring_s, FALSE, FALSE,
6209 EL_SPRING, ACTION_FALLING, -1
6212 Yspring_sB, FALSE, TRUE,
6213 EL_SPRING, ACTION_FALLING, -1
6216 Yspring_e, FALSE, FALSE,
6217 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6220 Yspring_eB, FALSE, TRUE,
6221 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6224 Yspring_w, FALSE, FALSE,
6225 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6228 Yspring_wB, FALSE, TRUE,
6229 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6232 Yspring_kill_e, FALSE, FALSE,
6233 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6236 Yspring_kill_eB, FALSE, TRUE,
6237 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6240 Yspring_kill_w, FALSE, FALSE,
6241 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6244 Yspring_kill_wB, FALSE, TRUE,
6245 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6248 Xeater_n, TRUE, FALSE,
6249 EL_YAMYAM_UP, -1, -1
6252 Xeater_e, TRUE, FALSE,
6253 EL_YAMYAM_RIGHT, -1, -1
6256 Xeater_w, TRUE, FALSE,
6257 EL_YAMYAM_LEFT, -1, -1
6260 Xeater_s, TRUE, FALSE,
6261 EL_YAMYAM_DOWN, -1, -1
6264 Yeater_n, FALSE, FALSE,
6265 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6268 Yeater_nB, FALSE, TRUE,
6269 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6272 Yeater_e, FALSE, FALSE,
6273 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6276 Yeater_eB, FALSE, TRUE,
6277 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6280 Yeater_s, FALSE, FALSE,
6281 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6284 Yeater_sB, FALSE, TRUE,
6285 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6288 Yeater_w, FALSE, FALSE,
6289 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6292 Yeater_wB, FALSE, TRUE,
6293 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6296 Yeater_stone, FALSE, FALSE,
6297 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6300 Yeater_spring, FALSE, FALSE,
6301 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6304 Xalien, TRUE, FALSE,
6308 Xalien_pause, FALSE, FALSE,
6312 Yalien_n, FALSE, FALSE,
6313 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6316 Yalien_nB, FALSE, TRUE,
6317 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6320 Yalien_e, FALSE, FALSE,
6321 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6324 Yalien_eB, FALSE, TRUE,
6325 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6328 Yalien_s, FALSE, FALSE,
6329 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6332 Yalien_sB, FALSE, TRUE,
6333 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6336 Yalien_w, FALSE, FALSE,
6337 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6340 Yalien_wB, FALSE, TRUE,
6341 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6344 Yalien_stone, FALSE, FALSE,
6345 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6348 Yalien_spring, FALSE, FALSE,
6349 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6352 Xemerald, TRUE, FALSE,
6356 Xemerald_pause, FALSE, FALSE,
6360 Xemerald_fall, FALSE, FALSE,
6364 Xemerald_shine, FALSE, FALSE,
6365 EL_EMERALD, ACTION_TWINKLING, -1
6368 Yemerald_s, FALSE, FALSE,
6369 EL_EMERALD, ACTION_FALLING, -1
6372 Yemerald_sB, FALSE, TRUE,
6373 EL_EMERALD, ACTION_FALLING, -1
6376 Yemerald_e, FALSE, FALSE,
6377 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6380 Yemerald_eB, FALSE, TRUE,
6381 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6384 Yemerald_w, FALSE, FALSE,
6385 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6388 Yemerald_wB, FALSE, TRUE,
6389 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6392 Yemerald_eat, FALSE, FALSE,
6393 EL_EMERALD, ACTION_COLLECTING, -1
6396 Yemerald_stone, FALSE, FALSE,
6397 EL_NUT, ACTION_BREAKING, -1
6400 Xdiamond, TRUE, FALSE,
6404 Xdiamond_pause, FALSE, FALSE,
6408 Xdiamond_fall, FALSE, FALSE,
6412 Xdiamond_shine, FALSE, FALSE,
6413 EL_DIAMOND, ACTION_TWINKLING, -1
6416 Ydiamond_s, FALSE, FALSE,
6417 EL_DIAMOND, ACTION_FALLING, -1
6420 Ydiamond_sB, FALSE, TRUE,
6421 EL_DIAMOND, ACTION_FALLING, -1
6424 Ydiamond_e, FALSE, FALSE,
6425 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6428 Ydiamond_eB, FALSE, TRUE,
6429 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6432 Ydiamond_w, FALSE, FALSE,
6433 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6436 Ydiamond_wB, FALSE, TRUE,
6437 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6440 Ydiamond_eat, FALSE, FALSE,
6441 EL_DIAMOND, ACTION_COLLECTING, -1
6444 Ydiamond_stone, FALSE, FALSE,
6445 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6448 Xdrip_fall, TRUE, FALSE,
6449 EL_AMOEBA_DROP, -1, -1
6452 Xdrip_stretch, FALSE, FALSE,
6453 EL_AMOEBA_DROP, ACTION_FALLING, -1
6456 Xdrip_stretchB, FALSE, TRUE,
6457 EL_AMOEBA_DROP, ACTION_FALLING, -1
6460 Xdrip_eat, FALSE, FALSE,
6461 EL_AMOEBA_DROP, ACTION_GROWING, -1
6464 Ydrip_s1, FALSE, FALSE,
6465 EL_AMOEBA_DROP, ACTION_FALLING, -1
6468 Ydrip_s1B, FALSE, TRUE,
6469 EL_AMOEBA_DROP, ACTION_FALLING, -1
6472 Ydrip_s2, FALSE, FALSE,
6473 EL_AMOEBA_DROP, ACTION_FALLING, -1
6476 Ydrip_s2B, FALSE, TRUE,
6477 EL_AMOEBA_DROP, ACTION_FALLING, -1
6484 Xbomb_pause, FALSE, FALSE,
6488 Xbomb_fall, FALSE, FALSE,
6492 Ybomb_s, FALSE, FALSE,
6493 EL_BOMB, ACTION_FALLING, -1
6496 Ybomb_sB, FALSE, TRUE,
6497 EL_BOMB, ACTION_FALLING, -1
6500 Ybomb_e, FALSE, FALSE,
6501 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6504 Ybomb_eB, FALSE, TRUE,
6505 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6508 Ybomb_w, FALSE, FALSE,
6509 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6512 Ybomb_wB, FALSE, TRUE,
6513 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6516 Ybomb_eat, FALSE, FALSE,
6517 EL_BOMB, ACTION_ACTIVATING, -1
6520 Xballoon, TRUE, FALSE,
6524 Yballoon_n, FALSE, FALSE,
6525 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6528 Yballoon_nB, FALSE, TRUE,
6529 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6532 Yballoon_e, FALSE, FALSE,
6533 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6536 Yballoon_eB, FALSE, TRUE,
6537 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6540 Yballoon_s, FALSE, FALSE,
6541 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6544 Yballoon_sB, FALSE, TRUE,
6545 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6548 Yballoon_w, FALSE, FALSE,
6549 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6552 Yballoon_wB, FALSE, TRUE,
6553 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6556 Xgrass, TRUE, FALSE,
6557 EL_EMC_GRASS, -1, -1
6560 Ygrass_nB, FALSE, FALSE,
6561 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6564 Ygrass_eB, FALSE, FALSE,
6565 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6568 Ygrass_sB, FALSE, FALSE,
6569 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6572 Ygrass_wB, FALSE, FALSE,
6573 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6580 Ydirt_nB, FALSE, FALSE,
6581 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6584 Ydirt_eB, FALSE, FALSE,
6585 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6588 Ydirt_sB, FALSE, FALSE,
6589 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6592 Ydirt_wB, FALSE, FALSE,
6593 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6596 Xacid_ne, TRUE, FALSE,
6597 EL_ACID_POOL_TOPRIGHT, -1, -1
6600 Xacid_se, TRUE, FALSE,
6601 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6604 Xacid_s, TRUE, FALSE,
6605 EL_ACID_POOL_BOTTOM, -1, -1
6608 Xacid_sw, TRUE, FALSE,
6609 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6612 Xacid_nw, TRUE, FALSE,
6613 EL_ACID_POOL_TOPLEFT, -1, -1
6616 Xacid_1, TRUE, FALSE,
6620 Xacid_2, FALSE, FALSE,
6624 Xacid_3, FALSE, FALSE,
6628 Xacid_4, FALSE, FALSE,
6632 Xacid_5, FALSE, FALSE,
6636 Xacid_6, FALSE, FALSE,
6640 Xacid_7, FALSE, FALSE,
6644 Xacid_8, FALSE, FALSE,
6648 Xball_1, TRUE, FALSE,
6649 EL_EMC_MAGIC_BALL, -1, -1
6652 Xball_1B, FALSE, FALSE,
6653 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6656 Xball_2, FALSE, FALSE,
6657 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6660 Xball_2B, FALSE, FALSE,
6661 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6664 Yball_eat, FALSE, FALSE,
6665 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6668 Ykey_1_eat, FALSE, FALSE,
6669 EL_EM_KEY_1, ACTION_COLLECTING, -1
6672 Ykey_2_eat, FALSE, FALSE,
6673 EL_EM_KEY_2, ACTION_COLLECTING, -1
6676 Ykey_3_eat, FALSE, FALSE,
6677 EL_EM_KEY_3, ACTION_COLLECTING, -1
6680 Ykey_4_eat, FALSE, FALSE,
6681 EL_EM_KEY_4, ACTION_COLLECTING, -1
6684 Ykey_5_eat, FALSE, FALSE,
6685 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6688 Ykey_6_eat, FALSE, FALSE,
6689 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6692 Ykey_7_eat, FALSE, FALSE,
6693 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6696 Ykey_8_eat, FALSE, FALSE,
6697 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6700 Ylenses_eat, FALSE, FALSE,
6701 EL_EMC_LENSES, ACTION_COLLECTING, -1
6704 Ymagnify_eat, FALSE, FALSE,
6705 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6708 Ygrass_eat, FALSE, FALSE,
6709 EL_EMC_GRASS, ACTION_SNAPPING, -1
6712 Ydirt_eat, FALSE, FALSE,
6713 EL_SAND, ACTION_SNAPPING, -1
6716 Xgrow_ns, TRUE, FALSE,
6717 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6720 Ygrow_ns_eat, FALSE, FALSE,
6721 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6724 Xgrow_ew, TRUE, FALSE,
6725 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6728 Ygrow_ew_eat, FALSE, FALSE,
6729 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6732 Xwonderwall, TRUE, FALSE,
6733 EL_MAGIC_WALL, -1, -1
6736 XwonderwallB, FALSE, FALSE,
6737 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6740 Xamoeba_1, TRUE, FALSE,
6741 EL_AMOEBA_DRY, ACTION_OTHER, -1
6744 Xamoeba_2, FALSE, FALSE,
6745 EL_AMOEBA_DRY, ACTION_OTHER, -1
6748 Xamoeba_3, FALSE, FALSE,
6749 EL_AMOEBA_DRY, ACTION_OTHER, -1
6752 Xamoeba_4, FALSE, FALSE,
6753 EL_AMOEBA_DRY, ACTION_OTHER, -1
6756 Xamoeba_5, TRUE, FALSE,
6757 EL_AMOEBA_WET, ACTION_OTHER, -1
6760 Xamoeba_6, FALSE, FALSE,
6761 EL_AMOEBA_WET, ACTION_OTHER, -1
6764 Xamoeba_7, FALSE, FALSE,
6765 EL_AMOEBA_WET, ACTION_OTHER, -1
6768 Xamoeba_8, FALSE, FALSE,
6769 EL_AMOEBA_WET, ACTION_OTHER, -1
6772 Xdoor_1, TRUE, FALSE,
6773 EL_EM_GATE_1, -1, -1
6776 Xdoor_2, TRUE, FALSE,
6777 EL_EM_GATE_2, -1, -1
6780 Xdoor_3, TRUE, FALSE,
6781 EL_EM_GATE_3, -1, -1
6784 Xdoor_4, TRUE, FALSE,
6785 EL_EM_GATE_4, -1, -1
6788 Xdoor_5, TRUE, FALSE,
6789 EL_EMC_GATE_5, -1, -1
6792 Xdoor_6, TRUE, FALSE,
6793 EL_EMC_GATE_6, -1, -1
6796 Xdoor_7, TRUE, FALSE,
6797 EL_EMC_GATE_7, -1, -1
6800 Xdoor_8, TRUE, FALSE,
6801 EL_EMC_GATE_8, -1, -1
6804 Xkey_1, TRUE, FALSE,
6808 Xkey_2, TRUE, FALSE,
6812 Xkey_3, TRUE, FALSE,
6816 Xkey_4, TRUE, FALSE,
6820 Xkey_5, TRUE, FALSE,
6821 EL_EMC_KEY_5, -1, -1
6824 Xkey_6, TRUE, FALSE,
6825 EL_EMC_KEY_6, -1, -1
6828 Xkey_7, TRUE, FALSE,
6829 EL_EMC_KEY_7, -1, -1
6832 Xkey_8, TRUE, FALSE,
6833 EL_EMC_KEY_8, -1, -1
6836 Xwind_n, TRUE, FALSE,
6837 EL_BALLOON_SWITCH_UP, -1, -1
6840 Xwind_e, TRUE, FALSE,
6841 EL_BALLOON_SWITCH_RIGHT, -1, -1
6844 Xwind_s, TRUE, FALSE,
6845 EL_BALLOON_SWITCH_DOWN, -1, -1
6848 Xwind_w, TRUE, FALSE,
6849 EL_BALLOON_SWITCH_LEFT, -1, -1
6852 Xwind_nesw, TRUE, FALSE,
6853 EL_BALLOON_SWITCH_ANY, -1, -1
6856 Xwind_stop, TRUE, FALSE,
6857 EL_BALLOON_SWITCH_NONE, -1, -1
6861 EL_EM_EXIT_CLOSED, -1, -1
6864 Xexit_1, TRUE, FALSE,
6865 EL_EM_EXIT_OPEN, -1, -1
6868 Xexit_2, FALSE, FALSE,
6869 EL_EM_EXIT_OPEN, -1, -1
6872 Xexit_3, FALSE, FALSE,
6873 EL_EM_EXIT_OPEN, -1, -1
6876 Xdynamite, TRUE, FALSE,
6877 EL_EM_DYNAMITE, -1, -1
6880 Ydynamite_eat, FALSE, FALSE,
6881 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6884 Xdynamite_1, TRUE, FALSE,
6885 EL_EM_DYNAMITE_ACTIVE, -1, -1
6888 Xdynamite_2, FALSE, FALSE,
6889 EL_EM_DYNAMITE_ACTIVE, -1, -1
6892 Xdynamite_3, FALSE, FALSE,
6893 EL_EM_DYNAMITE_ACTIVE, -1, -1
6896 Xdynamite_4, FALSE, FALSE,
6897 EL_EM_DYNAMITE_ACTIVE, -1, -1
6900 Xbumper, TRUE, FALSE,
6901 EL_EMC_SPRING_BUMPER, -1, -1
6904 XbumperB, FALSE, FALSE,
6905 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6908 Xwheel, TRUE, FALSE,
6909 EL_ROBOT_WHEEL, -1, -1
6912 XwheelB, FALSE, FALSE,
6913 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6916 Xswitch, TRUE, FALSE,
6917 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6920 XswitchB, FALSE, FALSE,
6921 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6925 EL_QUICKSAND_EMPTY, -1, -1
6928 Xsand_stone, TRUE, FALSE,
6929 EL_QUICKSAND_FULL, -1, -1
6932 Xsand_stonein_1, FALSE, TRUE,
6933 EL_ROCK, ACTION_FILLING, -1
6936 Xsand_stonein_2, FALSE, TRUE,
6937 EL_ROCK, ACTION_FILLING, -1
6940 Xsand_stonein_3, FALSE, TRUE,
6941 EL_ROCK, ACTION_FILLING, -1
6944 Xsand_stonein_4, FALSE, TRUE,
6945 EL_ROCK, ACTION_FILLING, -1
6948 Xsand_stonesand_1, FALSE, FALSE,
6949 EL_QUICKSAND_EMPTYING, -1, -1
6952 Xsand_stonesand_2, FALSE, FALSE,
6953 EL_QUICKSAND_EMPTYING, -1, -1
6956 Xsand_stonesand_3, FALSE, FALSE,
6957 EL_QUICKSAND_EMPTYING, -1, -1
6960 Xsand_stonesand_4, FALSE, FALSE,
6961 EL_QUICKSAND_EMPTYING, -1, -1
6964 Xsand_stonesand_quickout_1, FALSE, FALSE,
6965 EL_QUICKSAND_EMPTYING, -1, -1
6968 Xsand_stonesand_quickout_2, FALSE, FALSE,
6969 EL_QUICKSAND_EMPTYING, -1, -1
6972 Xsand_stoneout_1, FALSE, FALSE,
6973 EL_ROCK, ACTION_EMPTYING, -1
6976 Xsand_stoneout_2, FALSE, FALSE,
6977 EL_ROCK, ACTION_EMPTYING, -1
6980 Xsand_sandstone_1, FALSE, FALSE,
6981 EL_QUICKSAND_FILLING, -1, -1
6984 Xsand_sandstone_2, FALSE, FALSE,
6985 EL_QUICKSAND_FILLING, -1, -1
6988 Xsand_sandstone_3, FALSE, FALSE,
6989 EL_QUICKSAND_FILLING, -1, -1
6992 Xsand_sandstone_4, FALSE, FALSE,
6993 EL_QUICKSAND_FILLING, -1, -1
6996 Xplant, TRUE, FALSE,
6997 EL_EMC_PLANT, -1, -1
7000 Yplant, FALSE, FALSE,
7001 EL_EMC_PLANT, -1, -1
7004 Xlenses, TRUE, FALSE,
7005 EL_EMC_LENSES, -1, -1
7008 Xmagnify, TRUE, FALSE,
7009 EL_EMC_MAGNIFIER, -1, -1
7012 Xdripper, TRUE, FALSE,
7013 EL_EMC_DRIPPER, -1, -1
7016 XdripperB, FALSE, FALSE,
7017 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
7020 Xfake_blank, TRUE, FALSE,
7021 EL_INVISIBLE_WALL, -1, -1
7024 Xfake_blankB, FALSE, FALSE,
7025 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7028 Xfake_grass, TRUE, FALSE,
7029 EL_EMC_FAKE_GRASS, -1, -1
7032 Xfake_grassB, FALSE, FALSE,
7033 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7036 Xfake_door_1, TRUE, FALSE,
7037 EL_EM_GATE_1_GRAY, -1, -1
7040 Xfake_door_2, TRUE, FALSE,
7041 EL_EM_GATE_2_GRAY, -1, -1
7044 Xfake_door_3, TRUE, FALSE,
7045 EL_EM_GATE_3_GRAY, -1, -1
7048 Xfake_door_4, TRUE, FALSE,
7049 EL_EM_GATE_4_GRAY, -1, -1
7052 Xfake_door_5, TRUE, FALSE,
7053 EL_EMC_GATE_5_GRAY, -1, -1
7056 Xfake_door_6, TRUE, FALSE,
7057 EL_EMC_GATE_6_GRAY, -1, -1
7060 Xfake_door_7, TRUE, FALSE,
7061 EL_EMC_GATE_7_GRAY, -1, -1
7064 Xfake_door_8, TRUE, FALSE,
7065 EL_EMC_GATE_8_GRAY, -1, -1
7068 Xfake_acid_1, TRUE, FALSE,
7069 EL_EMC_FAKE_ACID, -1, -1
7072 Xfake_acid_2, FALSE, FALSE,
7073 EL_EMC_FAKE_ACID, -1, -1
7076 Xfake_acid_3, FALSE, FALSE,
7077 EL_EMC_FAKE_ACID, -1, -1
7080 Xfake_acid_4, FALSE, FALSE,
7081 EL_EMC_FAKE_ACID, -1, -1
7084 Xfake_acid_5, FALSE, FALSE,
7085 EL_EMC_FAKE_ACID, -1, -1
7088 Xfake_acid_6, FALSE, FALSE,
7089 EL_EMC_FAKE_ACID, -1, -1
7092 Xfake_acid_7, FALSE, FALSE,
7093 EL_EMC_FAKE_ACID, -1, -1
7096 Xfake_acid_8, FALSE, FALSE,
7097 EL_EMC_FAKE_ACID, -1, -1
7100 Xsteel_1, TRUE, FALSE,
7101 EL_STEELWALL, -1, -1
7104 Xsteel_2, TRUE, FALSE,
7105 EL_EMC_STEELWALL_2, -1, -1
7108 Xsteel_3, TRUE, FALSE,
7109 EL_EMC_STEELWALL_3, -1, -1
7112 Xsteel_4, TRUE, FALSE,
7113 EL_EMC_STEELWALL_4, -1, -1
7116 Xwall_1, TRUE, FALSE,
7120 Xwall_2, TRUE, FALSE,
7121 EL_EMC_WALL_14, -1, -1
7124 Xwall_3, TRUE, FALSE,
7125 EL_EMC_WALL_15, -1, -1
7128 Xwall_4, TRUE, FALSE,
7129 EL_EMC_WALL_16, -1, -1
7132 Xround_wall_1, TRUE, FALSE,
7133 EL_WALL_SLIPPERY, -1, -1
7136 Xround_wall_2, TRUE, FALSE,
7137 EL_EMC_WALL_SLIPPERY_2, -1, -1
7140 Xround_wall_3, TRUE, FALSE,
7141 EL_EMC_WALL_SLIPPERY_3, -1, -1
7144 Xround_wall_4, TRUE, FALSE,
7145 EL_EMC_WALL_SLIPPERY_4, -1, -1
7148 Xdecor_1, TRUE, FALSE,
7149 EL_EMC_WALL_8, -1, -1
7152 Xdecor_2, TRUE, FALSE,
7153 EL_EMC_WALL_6, -1, -1
7156 Xdecor_3, TRUE, FALSE,
7157 EL_EMC_WALL_4, -1, -1
7160 Xdecor_4, TRUE, FALSE,
7161 EL_EMC_WALL_7, -1, -1
7164 Xdecor_5, TRUE, FALSE,
7165 EL_EMC_WALL_5, -1, -1
7168 Xdecor_6, TRUE, FALSE,
7169 EL_EMC_WALL_9, -1, -1
7172 Xdecor_7, TRUE, FALSE,
7173 EL_EMC_WALL_10, -1, -1
7176 Xdecor_8, TRUE, FALSE,
7177 EL_EMC_WALL_1, -1, -1
7180 Xdecor_9, TRUE, FALSE,
7181 EL_EMC_WALL_2, -1, -1
7184 Xdecor_10, TRUE, FALSE,
7185 EL_EMC_WALL_3, -1, -1
7188 Xdecor_11, TRUE, FALSE,
7189 EL_EMC_WALL_11, -1, -1
7192 Xdecor_12, TRUE, FALSE,
7193 EL_EMC_WALL_12, -1, -1
7196 Xalpha_0, TRUE, FALSE,
7197 EL_CHAR('0'), -1, -1
7200 Xalpha_1, TRUE, FALSE,
7201 EL_CHAR('1'), -1, -1
7204 Xalpha_2, TRUE, FALSE,
7205 EL_CHAR('2'), -1, -1
7208 Xalpha_3, TRUE, FALSE,
7209 EL_CHAR('3'), -1, -1
7212 Xalpha_4, TRUE, FALSE,
7213 EL_CHAR('4'), -1, -1
7216 Xalpha_5, TRUE, FALSE,
7217 EL_CHAR('5'), -1, -1
7220 Xalpha_6, TRUE, FALSE,
7221 EL_CHAR('6'), -1, -1
7224 Xalpha_7, TRUE, FALSE,
7225 EL_CHAR('7'), -1, -1
7228 Xalpha_8, TRUE, FALSE,
7229 EL_CHAR('8'), -1, -1
7232 Xalpha_9, TRUE, FALSE,
7233 EL_CHAR('9'), -1, -1
7236 Xalpha_excla, TRUE, FALSE,
7237 EL_CHAR('!'), -1, -1
7240 Xalpha_quote, TRUE, FALSE,
7241 EL_CHAR('"'), -1, -1
7244 Xalpha_comma, TRUE, FALSE,
7245 EL_CHAR(','), -1, -1
7248 Xalpha_minus, TRUE, FALSE,
7249 EL_CHAR('-'), -1, -1
7252 Xalpha_perio, TRUE, FALSE,
7253 EL_CHAR('.'), -1, -1
7256 Xalpha_colon, TRUE, FALSE,
7257 EL_CHAR(':'), -1, -1
7260 Xalpha_quest, TRUE, FALSE,
7261 EL_CHAR('?'), -1, -1
7264 Xalpha_a, TRUE, FALSE,
7265 EL_CHAR('A'), -1, -1
7268 Xalpha_b, TRUE, FALSE,
7269 EL_CHAR('B'), -1, -1
7272 Xalpha_c, TRUE, FALSE,
7273 EL_CHAR('C'), -1, -1
7276 Xalpha_d, TRUE, FALSE,
7277 EL_CHAR('D'), -1, -1
7280 Xalpha_e, TRUE, FALSE,
7281 EL_CHAR('E'), -1, -1
7284 Xalpha_f, TRUE, FALSE,
7285 EL_CHAR('F'), -1, -1
7288 Xalpha_g, TRUE, FALSE,
7289 EL_CHAR('G'), -1, -1
7292 Xalpha_h, TRUE, FALSE,
7293 EL_CHAR('H'), -1, -1
7296 Xalpha_i, TRUE, FALSE,
7297 EL_CHAR('I'), -1, -1
7300 Xalpha_j, TRUE, FALSE,
7301 EL_CHAR('J'), -1, -1
7304 Xalpha_k, TRUE, FALSE,
7305 EL_CHAR('K'), -1, -1
7308 Xalpha_l, TRUE, FALSE,
7309 EL_CHAR('L'), -1, -1
7312 Xalpha_m, TRUE, FALSE,
7313 EL_CHAR('M'), -1, -1
7316 Xalpha_n, TRUE, FALSE,
7317 EL_CHAR('N'), -1, -1
7320 Xalpha_o, TRUE, FALSE,
7321 EL_CHAR('O'), -1, -1
7324 Xalpha_p, TRUE, FALSE,
7325 EL_CHAR('P'), -1, -1
7328 Xalpha_q, TRUE, FALSE,
7329 EL_CHAR('Q'), -1, -1
7332 Xalpha_r, TRUE, FALSE,
7333 EL_CHAR('R'), -1, -1
7336 Xalpha_s, TRUE, FALSE,
7337 EL_CHAR('S'), -1, -1
7340 Xalpha_t, TRUE, FALSE,
7341 EL_CHAR('T'), -1, -1
7344 Xalpha_u, TRUE, FALSE,
7345 EL_CHAR('U'), -1, -1
7348 Xalpha_v, TRUE, FALSE,
7349 EL_CHAR('V'), -1, -1
7352 Xalpha_w, TRUE, FALSE,
7353 EL_CHAR('W'), -1, -1
7356 Xalpha_x, TRUE, FALSE,
7357 EL_CHAR('X'), -1, -1
7360 Xalpha_y, TRUE, FALSE,
7361 EL_CHAR('Y'), -1, -1
7364 Xalpha_z, TRUE, FALSE,
7365 EL_CHAR('Z'), -1, -1
7368 Xalpha_arrow_e, TRUE, FALSE,
7369 EL_CHAR('>'), -1, -1
7372 Xalpha_arrow_w, TRUE, FALSE,
7373 EL_CHAR('<'), -1, -1
7376 Xalpha_copyr, TRUE, FALSE,
7377 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7381 Xboom_bug, FALSE, FALSE,
7382 EL_BUG, ACTION_EXPLODING, -1
7385 Xboom_bomb, FALSE, FALSE,
7386 EL_BOMB, ACTION_EXPLODING, -1
7389 Xboom_android, FALSE, FALSE,
7390 EL_EMC_ANDROID, ACTION_OTHER, -1
7393 Xboom_1, FALSE, FALSE,
7394 EL_DEFAULT, ACTION_EXPLODING, -1
7397 Xboom_2, FALSE, FALSE,
7398 EL_DEFAULT, ACTION_EXPLODING, -1
7401 Znormal, FALSE, FALSE,
7405 Zdynamite, FALSE, FALSE,
7409 Zplayer, FALSE, FALSE,
7413 ZBORDER, FALSE, FALSE,
7423 static struct Mapping_EM_to_RND_player
7432 em_player_mapping_list[] =
7436 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7440 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7444 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7448 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7452 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7456 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7460 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7464 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7468 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7472 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7476 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7480 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7484 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7488 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7492 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7496 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7500 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7504 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7508 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7512 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7516 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7520 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7524 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7528 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7532 EL_PLAYER_1, ACTION_DEFAULT, -1,
7536 EL_PLAYER_2, ACTION_DEFAULT, -1,
7540 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7544 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7548 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7552 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7556 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7560 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7564 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7568 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7572 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7576 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7580 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7584 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7588 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7592 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7596 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7600 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7604 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7608 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7612 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7616 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7620 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7624 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7628 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7632 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7636 EL_PLAYER_3, ACTION_DEFAULT, -1,
7640 EL_PLAYER_4, ACTION_DEFAULT, -1,
7649 int map_element_RND_to_EM(int element_rnd)
7651 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7652 static boolean mapping_initialized = FALSE;
7654 if (!mapping_initialized)
7658 // return "Xalpha_quest" for all undefined elements in mapping array
7659 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7660 mapping_RND_to_EM[i] = Xalpha_quest;
7662 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7663 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7664 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7665 em_object_mapping_list[i].element_em;
7667 mapping_initialized = TRUE;
7670 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7671 return mapping_RND_to_EM[element_rnd];
7673 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7678 int map_element_EM_to_RND(int element_em)
7680 static unsigned short mapping_EM_to_RND[TILE_MAX];
7681 static boolean mapping_initialized = FALSE;
7683 if (!mapping_initialized)
7687 // return "EL_UNKNOWN" for all undefined elements in mapping array
7688 for (i = 0; i < TILE_MAX; i++)
7689 mapping_EM_to_RND[i] = EL_UNKNOWN;
7691 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7692 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7693 em_object_mapping_list[i].element_rnd;
7695 mapping_initialized = TRUE;
7698 if (element_em >= 0 && element_em < TILE_MAX)
7699 return mapping_EM_to_RND[element_em];
7701 Error(ERR_WARN, "invalid EM level element %d", element_em);
7706 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7708 struct LevelInfo_EM *level_em = level->native_em_level;
7709 struct LEVEL *lev = level_em->lev;
7712 for (i = 0; i < TILE_MAX; i++)
7713 lev->android_array[i] = Xblank;
7715 for (i = 0; i < level->num_android_clone_elements; i++)
7717 int element_rnd = level->android_clone_element[i];
7718 int element_em = map_element_RND_to_EM(element_rnd);
7720 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7721 if (em_object_mapping_list[j].element_rnd == element_rnd)
7722 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7726 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7728 struct LevelInfo_EM *level_em = level->native_em_level;
7729 struct LEVEL *lev = level_em->lev;
7732 level->num_android_clone_elements = 0;
7734 for (i = 0; i < TILE_MAX; i++)
7736 int element_em = lev->android_array[i];
7738 boolean element_found = FALSE;
7740 if (element_em == Xblank)
7743 element_rnd = map_element_EM_to_RND(element_em);
7745 for (j = 0; j < level->num_android_clone_elements; j++)
7746 if (level->android_clone_element[j] == element_rnd)
7747 element_found = TRUE;
7751 level->android_clone_element[level->num_android_clone_elements++] =
7754 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7759 if (level->num_android_clone_elements == 0)
7761 level->num_android_clone_elements = 1;
7762 level->android_clone_element[0] = EL_EMPTY;
7766 int map_direction_RND_to_EM(int direction)
7768 return (direction == MV_UP ? 0 :
7769 direction == MV_RIGHT ? 1 :
7770 direction == MV_DOWN ? 2 :
7771 direction == MV_LEFT ? 3 :
7775 int map_direction_EM_to_RND(int direction)
7777 return (direction == 0 ? MV_UP :
7778 direction == 1 ? MV_RIGHT :
7779 direction == 2 ? MV_DOWN :
7780 direction == 3 ? MV_LEFT :
7784 int map_element_RND_to_SP(int element_rnd)
7786 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7788 if (element_rnd >= EL_SP_START &&
7789 element_rnd <= EL_SP_END)
7790 element_sp = element_rnd - EL_SP_START;
7791 else if (element_rnd == EL_EMPTY_SPACE)
7793 else if (element_rnd == EL_INVISIBLE_WALL)
7799 int map_element_SP_to_RND(int element_sp)
7801 int element_rnd = EL_UNKNOWN;
7803 if (element_sp >= 0x00 &&
7805 element_rnd = EL_SP_START + element_sp;
7806 else if (element_sp == 0x28)
7807 element_rnd = EL_INVISIBLE_WALL;
7812 int map_action_SP_to_RND(int action_sp)
7816 case actActive: return ACTION_ACTIVE;
7817 case actImpact: return ACTION_IMPACT;
7818 case actExploding: return ACTION_EXPLODING;
7819 case actDigging: return ACTION_DIGGING;
7820 case actSnapping: return ACTION_SNAPPING;
7821 case actCollecting: return ACTION_COLLECTING;
7822 case actPassing: return ACTION_PASSING;
7823 case actPushing: return ACTION_PUSHING;
7824 case actDropping: return ACTION_DROPPING;
7826 default: return ACTION_DEFAULT;
7830 int map_element_RND_to_MM(int element_rnd)
7832 return (element_rnd >= EL_MM_START_1 &&
7833 element_rnd <= EL_MM_END_1 ?
7834 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7836 element_rnd >= EL_MM_START_2 &&
7837 element_rnd <= EL_MM_END_2 ?
7838 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7840 element_rnd >= EL_CHAR_START &&
7841 element_rnd <= EL_CHAR_END ?
7842 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7844 element_rnd >= EL_MM_RUNTIME_START &&
7845 element_rnd <= EL_MM_RUNTIME_END ?
7846 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7848 element_rnd >= EL_MM_DUMMY_START &&
7849 element_rnd <= EL_MM_DUMMY_END ?
7850 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7852 EL_MM_EMPTY_NATIVE);
7855 int map_element_MM_to_RND(int element_mm)
7857 return (element_mm == EL_MM_EMPTY_NATIVE ||
7858 element_mm == EL_DF_EMPTY_NATIVE ?
7861 element_mm >= EL_MM_START_1_NATIVE &&
7862 element_mm <= EL_MM_END_1_NATIVE ?
7863 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7865 element_mm >= EL_MM_START_2_NATIVE &&
7866 element_mm <= EL_MM_END_2_NATIVE ?
7867 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7869 element_mm >= EL_MM_CHAR_START_NATIVE &&
7870 element_mm <= EL_MM_CHAR_END_NATIVE ?
7871 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7873 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7874 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7875 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7877 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7878 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7879 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7884 int map_action_MM_to_RND(int action_mm)
7886 // all MM actions are defined to exactly match their RND counterparts
7890 int map_sound_MM_to_RND(int sound_mm)
7894 case SND_MM_GAME_LEVELTIME_CHARGING:
7895 return SND_GAME_LEVELTIME_CHARGING;
7897 case SND_MM_GAME_HEALTH_CHARGING:
7898 return SND_GAME_HEALTH_CHARGING;
7901 return SND_UNDEFINED;
7905 int map_mm_wall_element(int element)
7907 return (element >= EL_MM_STEEL_WALL_START &&
7908 element <= EL_MM_STEEL_WALL_END ?
7911 element >= EL_MM_WOODEN_WALL_START &&
7912 element <= EL_MM_WOODEN_WALL_END ?
7915 element >= EL_MM_ICE_WALL_START &&
7916 element <= EL_MM_ICE_WALL_END ?
7919 element >= EL_MM_AMOEBA_WALL_START &&
7920 element <= EL_MM_AMOEBA_WALL_END ?
7923 element >= EL_DF_STEEL_WALL_START &&
7924 element <= EL_DF_STEEL_WALL_END ?
7927 element >= EL_DF_WOODEN_WALL_START &&
7928 element <= EL_DF_WOODEN_WALL_END ?
7934 int map_mm_wall_element_editor(int element)
7938 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7939 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7940 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7941 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7942 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7943 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7945 default: return element;
7949 int get_next_element(int element)
7953 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7954 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7955 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7956 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7957 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7958 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7959 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7960 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7961 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7962 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7963 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7965 default: return element;
7969 int el2img_mm(int element_mm)
7971 return el2img(map_element_MM_to_RND(element_mm));
7974 int el_act_dir2img(int element, int action, int direction)
7976 element = GFX_ELEMENT(element);
7977 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7979 // direction_graphic[][] == graphic[] for undefined direction graphics
7980 return element_info[element].direction_graphic[action][direction];
7983 static int el_act_dir2crm(int element, int action, int direction)
7985 element = GFX_ELEMENT(element);
7986 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7988 // direction_graphic[][] == graphic[] for undefined direction graphics
7989 return element_info[element].direction_crumbled[action][direction];
7992 int el_act2img(int element, int action)
7994 element = GFX_ELEMENT(element);
7996 return element_info[element].graphic[action];
7999 int el_act2crm(int element, int action)
8001 element = GFX_ELEMENT(element);
8003 return element_info[element].crumbled[action];
8006 int el_dir2img(int element, int direction)
8008 element = GFX_ELEMENT(element);
8010 return el_act_dir2img(element, ACTION_DEFAULT, direction);
8013 int el2baseimg(int element)
8015 return element_info[element].graphic[ACTION_DEFAULT];
8018 int el2img(int element)
8020 element = GFX_ELEMENT(element);
8022 return element_info[element].graphic[ACTION_DEFAULT];
8025 int el2edimg(int element)
8027 element = GFX_ELEMENT(element);
8029 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8032 int el2preimg(int element)
8034 element = GFX_ELEMENT(element);
8036 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8039 int el2panelimg(int element)
8041 element = GFX_ELEMENT(element);
8043 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8046 int font2baseimg(int font_nr)
8048 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8051 int getBeltNrFromBeltElement(int element)
8053 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8054 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8055 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8058 int getBeltNrFromBeltActiveElement(int element)
8060 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8061 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8062 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8065 int getBeltNrFromBeltSwitchElement(int element)
8067 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8068 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8069 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8072 int getBeltDirNrFromBeltElement(int element)
8074 static int belt_base_element[4] =
8076 EL_CONVEYOR_BELT_1_LEFT,
8077 EL_CONVEYOR_BELT_2_LEFT,
8078 EL_CONVEYOR_BELT_3_LEFT,
8079 EL_CONVEYOR_BELT_4_LEFT
8082 int belt_nr = getBeltNrFromBeltElement(element);
8083 int belt_dir_nr = element - belt_base_element[belt_nr];
8085 return (belt_dir_nr % 3);
8088 int getBeltDirNrFromBeltSwitchElement(int element)
8090 static int belt_base_element[4] =
8092 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8093 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8094 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8095 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8098 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8099 int belt_dir_nr = element - belt_base_element[belt_nr];
8101 return (belt_dir_nr % 3);
8104 int getBeltDirFromBeltElement(int element)
8106 static int belt_move_dir[3] =
8113 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8115 return belt_move_dir[belt_dir_nr];
8118 int getBeltDirFromBeltSwitchElement(int element)
8120 static int belt_move_dir[3] =
8127 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8129 return belt_move_dir[belt_dir_nr];
8132 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8134 static int belt_base_element[4] =
8136 EL_CONVEYOR_BELT_1_LEFT,
8137 EL_CONVEYOR_BELT_2_LEFT,
8138 EL_CONVEYOR_BELT_3_LEFT,
8139 EL_CONVEYOR_BELT_4_LEFT
8142 return belt_base_element[belt_nr] + belt_dir_nr;
8145 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8147 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8149 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8152 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8154 static int belt_base_element[4] =
8156 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8157 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8158 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8159 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8162 return belt_base_element[belt_nr] + belt_dir_nr;
8165 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8167 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8169 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8172 boolean getTeamMode_EM(void)
8174 return game.team_mode || network_playing;
8177 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8179 int game_frame_delay_value;
8181 game_frame_delay_value =
8182 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8183 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8186 if (tape.playing && tape.warp_forward && !tape.pausing)
8187 game_frame_delay_value = 0;
8189 return game_frame_delay_value;
8192 unsigned int InitRND(int seed)
8194 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8195 return InitEngineRandom_EM(seed);
8196 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8197 return InitEngineRandom_SP(seed);
8198 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8199 return InitEngineRandom_MM(seed);
8201 return InitEngineRandom_RND(seed);
8204 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8205 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8207 static int get_effective_element_EM(int tile, int frame_em)
8209 int element = object_mapping[tile].element_rnd;
8210 int action = object_mapping[tile].action;
8211 boolean is_backside = object_mapping[tile].is_backside;
8212 boolean action_removing = (action == ACTION_DIGGING ||
8213 action == ACTION_SNAPPING ||
8214 action == ACTION_COLLECTING);
8220 case Yacid_splash_eB:
8221 case Yacid_splash_wB:
8222 return (frame_em > 5 ? EL_EMPTY : element);
8228 else // frame_em == 7
8232 case Yacid_splash_eB:
8233 case Yacid_splash_wB:
8236 case Yemerald_stone:
8239 case Ydiamond_stone:
8243 case Xdrip_stretchB:
8262 case Xsand_stonein_1:
8263 case Xsand_stonein_2:
8264 case Xsand_stonein_3:
8265 case Xsand_stonein_4:
8269 return (is_backside || action_removing ? EL_EMPTY : element);
8274 static boolean check_linear_animation_EM(int tile)
8278 case Xsand_stonesand_1:
8279 case Xsand_stonesand_quickout_1:
8280 case Xsand_sandstone_1:
8281 case Xsand_stonein_1:
8282 case Xsand_stoneout_1:
8301 case Yacid_splash_eB:
8302 case Yacid_splash_wB:
8303 case Yemerald_stone:
8310 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8311 boolean has_crumbled_graphics,
8312 int crumbled, int sync_frame)
8314 // if element can be crumbled, but certain action graphics are just empty
8315 // space (like instantly snapping sand to empty space in 1 frame), do not
8316 // treat these empty space graphics as crumbled graphics in EMC engine
8317 if (crumbled == IMG_EMPTY_SPACE)
8318 has_crumbled_graphics = FALSE;
8320 if (has_crumbled_graphics)
8322 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8323 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8324 g_crumbled->anim_delay,
8325 g_crumbled->anim_mode,
8326 g_crumbled->anim_start_frame,
8329 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8330 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8332 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8333 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8335 g_em->has_crumbled_graphics = TRUE;
8339 g_em->crumbled_bitmap = NULL;
8340 g_em->crumbled_src_x = 0;
8341 g_em->crumbled_src_y = 0;
8342 g_em->crumbled_border_size = 0;
8343 g_em->crumbled_tile_size = 0;
8345 g_em->has_crumbled_graphics = FALSE;
8350 void ResetGfxAnimation_EM(int x, int y, int tile)
8356 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8357 int tile, int frame_em, int x, int y)
8359 int action = object_mapping[tile].action;
8360 int direction = object_mapping[tile].direction;
8361 int effective_element = get_effective_element_EM(tile, frame_em);
8362 int graphic = (direction == MV_NONE ?
8363 el_act2img(effective_element, action) :
8364 el_act_dir2img(effective_element, action, direction));
8365 struct GraphicInfo *g = &graphic_info[graphic];
8367 boolean action_removing = (action == ACTION_DIGGING ||
8368 action == ACTION_SNAPPING ||
8369 action == ACTION_COLLECTING);
8370 boolean action_moving = (action == ACTION_FALLING ||
8371 action == ACTION_MOVING ||
8372 action == ACTION_PUSHING ||
8373 action == ACTION_EATING ||
8374 action == ACTION_FILLING ||
8375 action == ACTION_EMPTYING);
8376 boolean action_falling = (action == ACTION_FALLING ||
8377 action == ACTION_FILLING ||
8378 action == ACTION_EMPTYING);
8380 // special case: graphic uses "2nd movement tile" and has defined
8381 // 7 frames for movement animation (or less) => use default graphic
8382 // for last (8th) frame which ends the movement animation
8383 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8385 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8386 graphic = (direction == MV_NONE ?
8387 el_act2img(effective_element, action) :
8388 el_act_dir2img(effective_element, action, direction));
8390 g = &graphic_info[graphic];
8393 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8397 else if (action_moving)
8399 boolean is_backside = object_mapping[tile].is_backside;
8403 int direction = object_mapping[tile].direction;
8404 int move_dir = (action_falling ? MV_DOWN : direction);
8409 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8410 if (g->double_movement && frame_em == 0)
8414 if (move_dir == MV_LEFT)
8415 GfxFrame[x - 1][y] = GfxFrame[x][y];
8416 else if (move_dir == MV_RIGHT)
8417 GfxFrame[x + 1][y] = GfxFrame[x][y];
8418 else if (move_dir == MV_UP)
8419 GfxFrame[x][y - 1] = GfxFrame[x][y];
8420 else if (move_dir == MV_DOWN)
8421 GfxFrame[x][y + 1] = GfxFrame[x][y];
8428 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8429 if (tile == Xsand_stonesand_quickout_1 ||
8430 tile == Xsand_stonesand_quickout_2)
8434 if (graphic_info[graphic].anim_global_sync)
8435 sync_frame = FrameCounter;
8436 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8437 sync_frame = GfxFrame[x][y];
8439 sync_frame = 0; // playfield border (pseudo steel)
8441 SetRandomAnimationValue(x, y);
8443 int frame = getAnimationFrame(g->anim_frames,
8446 g->anim_start_frame,
8449 g_em->unique_identifier =
8450 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8453 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8454 int tile, int frame_em, int x, int y)
8456 int action = object_mapping[tile].action;
8457 int direction = object_mapping[tile].direction;
8458 boolean is_backside = object_mapping[tile].is_backside;
8459 int effective_element = get_effective_element_EM(tile, frame_em);
8460 int effective_action = action;
8461 int graphic = (direction == MV_NONE ?
8462 el_act2img(effective_element, effective_action) :
8463 el_act_dir2img(effective_element, effective_action,
8465 int crumbled = (direction == MV_NONE ?
8466 el_act2crm(effective_element, effective_action) :
8467 el_act_dir2crm(effective_element, effective_action,
8469 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8470 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8471 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8472 struct GraphicInfo *g = &graphic_info[graphic];
8475 // special case: graphic uses "2nd movement tile" and has defined
8476 // 7 frames for movement animation (or less) => use default graphic
8477 // for last (8th) frame which ends the movement animation
8478 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8480 effective_action = ACTION_DEFAULT;
8481 graphic = (direction == MV_NONE ?
8482 el_act2img(effective_element, effective_action) :
8483 el_act_dir2img(effective_element, effective_action,
8485 crumbled = (direction == MV_NONE ?
8486 el_act2crm(effective_element, effective_action) :
8487 el_act_dir2crm(effective_element, effective_action,
8490 g = &graphic_info[graphic];
8493 if (graphic_info[graphic].anim_global_sync)
8494 sync_frame = FrameCounter;
8495 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8496 sync_frame = GfxFrame[x][y];
8498 sync_frame = 0; // playfield border (pseudo steel)
8500 SetRandomAnimationValue(x, y);
8502 int frame = getAnimationFrame(g->anim_frames,
8505 g->anim_start_frame,
8508 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8509 g->double_movement && is_backside);
8511 // (updating the "crumbled" graphic definitions is probably not really needed,
8512 // as animations for crumbled graphics can't be longer than one EMC cycle)
8513 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8517 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8518 int player_nr, int anim, int frame_em)
8520 int element = player_mapping[player_nr][anim].element_rnd;
8521 int action = player_mapping[player_nr][anim].action;
8522 int direction = player_mapping[player_nr][anim].direction;
8523 int graphic = (direction == MV_NONE ?
8524 el_act2img(element, action) :
8525 el_act_dir2img(element, action, direction));
8526 struct GraphicInfo *g = &graphic_info[graphic];
8529 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8531 stored_player[player_nr].StepFrame = frame_em;
8533 sync_frame = stored_player[player_nr].Frame;
8535 int frame = getAnimationFrame(g->anim_frames,
8538 g->anim_start_frame,
8541 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8542 &g_em->src_x, &g_em->src_y, FALSE);
8545 void InitGraphicInfo_EM(void)
8550 int num_em_gfx_errors = 0;
8552 if (graphic_info_em_object[0][0].bitmap == NULL)
8554 // EM graphics not yet initialized in em_open_all()
8559 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8562 // always start with reliable default values
8563 for (i = 0; i < TILE_MAX; i++)
8565 object_mapping[i].element_rnd = EL_UNKNOWN;
8566 object_mapping[i].is_backside = FALSE;
8567 object_mapping[i].action = ACTION_DEFAULT;
8568 object_mapping[i].direction = MV_NONE;
8571 // always start with reliable default values
8572 for (p = 0; p < MAX_PLAYERS; p++)
8574 for (i = 0; i < SPR_MAX; i++)
8576 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8577 player_mapping[p][i].action = ACTION_DEFAULT;
8578 player_mapping[p][i].direction = MV_NONE;
8582 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8584 int e = em_object_mapping_list[i].element_em;
8586 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8587 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8589 if (em_object_mapping_list[i].action != -1)
8590 object_mapping[e].action = em_object_mapping_list[i].action;
8592 if (em_object_mapping_list[i].direction != -1)
8593 object_mapping[e].direction =
8594 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8597 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8599 int a = em_player_mapping_list[i].action_em;
8600 int p = em_player_mapping_list[i].player_nr;
8602 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8604 if (em_player_mapping_list[i].action != -1)
8605 player_mapping[p][a].action = em_player_mapping_list[i].action;
8607 if (em_player_mapping_list[i].direction != -1)
8608 player_mapping[p][a].direction =
8609 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8612 for (i = 0; i < TILE_MAX; i++)
8614 int element = object_mapping[i].element_rnd;
8615 int action = object_mapping[i].action;
8616 int direction = object_mapping[i].direction;
8617 boolean is_backside = object_mapping[i].is_backside;
8618 boolean action_exploding = ((action == ACTION_EXPLODING ||
8619 action == ACTION_SMASHED_BY_ROCK ||
8620 action == ACTION_SMASHED_BY_SPRING) &&
8621 element != EL_DIAMOND);
8622 boolean action_active = (action == ACTION_ACTIVE);
8623 boolean action_other = (action == ACTION_OTHER);
8625 for (j = 0; j < 8; j++)
8627 int effective_element = get_effective_element_EM(i, j);
8628 int effective_action = (j < 7 ? action :
8629 i == Xdrip_stretch ? action :
8630 i == Xdrip_stretchB ? action :
8631 i == Ydrip_s1 ? action :
8632 i == Ydrip_s1B ? action :
8633 i == Xball_1B ? action :
8634 i == Xball_2 ? action :
8635 i == Xball_2B ? action :
8636 i == Yball_eat ? action :
8637 i == Ykey_1_eat ? action :
8638 i == Ykey_2_eat ? action :
8639 i == Ykey_3_eat ? action :
8640 i == Ykey_4_eat ? action :
8641 i == Ykey_5_eat ? action :
8642 i == Ykey_6_eat ? action :
8643 i == Ykey_7_eat ? action :
8644 i == Ykey_8_eat ? action :
8645 i == Ylenses_eat ? action :
8646 i == Ymagnify_eat ? action :
8647 i == Ygrass_eat ? action :
8648 i == Ydirt_eat ? action :
8649 i == Xsand_stonein_1 ? action :
8650 i == Xsand_stonein_2 ? action :
8651 i == Xsand_stonein_3 ? action :
8652 i == Xsand_stonein_4 ? action :
8653 i == Xsand_stoneout_1 ? action :
8654 i == Xsand_stoneout_2 ? action :
8655 i == Xboom_android ? ACTION_EXPLODING :
8656 action_exploding ? ACTION_EXPLODING :
8657 action_active ? action :
8658 action_other ? action :
8660 int graphic = (el_act_dir2img(effective_element, effective_action,
8662 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8664 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8665 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8666 boolean has_action_graphics = (graphic != base_graphic);
8667 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8668 struct GraphicInfo *g = &graphic_info[graphic];
8669 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8672 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8673 boolean special_animation = (action != ACTION_DEFAULT &&
8674 g->anim_frames == 3 &&
8675 g->anim_delay == 2 &&
8676 g->anim_mode & ANIM_LINEAR);
8677 int sync_frame = (i == Xdrip_stretch ? 7 :
8678 i == Xdrip_stretchB ? 7 :
8679 i == Ydrip_s2 ? j + 8 :
8680 i == Ydrip_s2B ? j + 8 :
8689 i == Xfake_acid_1 ? 0 :
8690 i == Xfake_acid_2 ? 10 :
8691 i == Xfake_acid_3 ? 20 :
8692 i == Xfake_acid_4 ? 30 :
8693 i == Xfake_acid_5 ? 40 :
8694 i == Xfake_acid_6 ? 50 :
8695 i == Xfake_acid_7 ? 60 :
8696 i == Xfake_acid_8 ? 70 :
8698 i == Xball_2B ? j + 8 :
8699 i == Yball_eat ? j + 1 :
8700 i == Ykey_1_eat ? j + 1 :
8701 i == Ykey_2_eat ? j + 1 :
8702 i == Ykey_3_eat ? j + 1 :
8703 i == Ykey_4_eat ? j + 1 :
8704 i == Ykey_5_eat ? j + 1 :
8705 i == Ykey_6_eat ? j + 1 :
8706 i == Ykey_7_eat ? j + 1 :
8707 i == Ykey_8_eat ? j + 1 :
8708 i == Ylenses_eat ? j + 1 :
8709 i == Ymagnify_eat ? j + 1 :
8710 i == Ygrass_eat ? j + 1 :
8711 i == Ydirt_eat ? j + 1 :
8712 i == Xamoeba_1 ? 0 :
8713 i == Xamoeba_2 ? 1 :
8714 i == Xamoeba_3 ? 2 :
8715 i == Xamoeba_4 ? 3 :
8716 i == Xamoeba_5 ? 0 :
8717 i == Xamoeba_6 ? 1 :
8718 i == Xamoeba_7 ? 2 :
8719 i == Xamoeba_8 ? 3 :
8720 i == Xexit_2 ? j + 8 :
8721 i == Xexit_3 ? j + 16 :
8722 i == Xdynamite_1 ? 0 :
8723 i == Xdynamite_2 ? 8 :
8724 i == Xdynamite_3 ? 16 :
8725 i == Xdynamite_4 ? 24 :
8726 i == Xsand_stonein_1 ? j + 1 :
8727 i == Xsand_stonein_2 ? j + 9 :
8728 i == Xsand_stonein_3 ? j + 17 :
8729 i == Xsand_stonein_4 ? j + 25 :
8730 i == Xsand_stoneout_1 && j == 0 ? 0 :
8731 i == Xsand_stoneout_1 && j == 1 ? 0 :
8732 i == Xsand_stoneout_1 && j == 2 ? 1 :
8733 i == Xsand_stoneout_1 && j == 3 ? 2 :
8734 i == Xsand_stoneout_1 && j == 4 ? 2 :
8735 i == Xsand_stoneout_1 && j == 5 ? 3 :
8736 i == Xsand_stoneout_1 && j == 6 ? 4 :
8737 i == Xsand_stoneout_1 && j == 7 ? 4 :
8738 i == Xsand_stoneout_2 && j == 0 ? 5 :
8739 i == Xsand_stoneout_2 && j == 1 ? 6 :
8740 i == Xsand_stoneout_2 && j == 2 ? 7 :
8741 i == Xsand_stoneout_2 && j == 3 ? 8 :
8742 i == Xsand_stoneout_2 && j == 4 ? 9 :
8743 i == Xsand_stoneout_2 && j == 5 ? 11 :
8744 i == Xsand_stoneout_2 && j == 6 ? 13 :
8745 i == Xsand_stoneout_2 && j == 7 ? 15 :
8746 i == Xboom_bug && j == 1 ? 2 :
8747 i == Xboom_bug && j == 2 ? 2 :
8748 i == Xboom_bug && j == 3 ? 4 :
8749 i == Xboom_bug && j == 4 ? 4 :
8750 i == Xboom_bug && j == 5 ? 2 :
8751 i == Xboom_bug && j == 6 ? 2 :
8752 i == Xboom_bug && j == 7 ? 0 :
8753 i == Xboom_bomb && j == 1 ? 2 :
8754 i == Xboom_bomb && j == 2 ? 2 :
8755 i == Xboom_bomb && j == 3 ? 4 :
8756 i == Xboom_bomb && j == 4 ? 4 :
8757 i == Xboom_bomb && j == 5 ? 2 :
8758 i == Xboom_bomb && j == 6 ? 2 :
8759 i == Xboom_bomb && j == 7 ? 0 :
8760 i == Xboom_android && j == 7 ? 6 :
8761 i == Xboom_1 && j == 1 ? 2 :
8762 i == Xboom_1 && j == 2 ? 2 :
8763 i == Xboom_1 && j == 3 ? 4 :
8764 i == Xboom_1 && j == 4 ? 4 :
8765 i == Xboom_1 && j == 5 ? 6 :
8766 i == Xboom_1 && j == 6 ? 6 :
8767 i == Xboom_1 && j == 7 ? 8 :
8768 i == Xboom_2 && j == 0 ? 8 :
8769 i == Xboom_2 && j == 1 ? 8 :
8770 i == Xboom_2 && j == 2 ? 10 :
8771 i == Xboom_2 && j == 3 ? 10 :
8772 i == Xboom_2 && j == 4 ? 10 :
8773 i == Xboom_2 && j == 5 ? 12 :
8774 i == Xboom_2 && j == 6 ? 12 :
8775 i == Xboom_2 && j == 7 ? 12 :
8776 special_animation && j == 4 ? 3 :
8777 effective_action != action ? 0 :
8781 Bitmap *debug_bitmap = g_em->bitmap;
8782 int debug_src_x = g_em->src_x;
8783 int debug_src_y = g_em->src_y;
8786 int frame = getAnimationFrame(g->anim_frames,
8789 g->anim_start_frame,
8792 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8793 g->double_movement && is_backside);
8795 g_em->bitmap = src_bitmap;
8796 g_em->src_x = src_x;
8797 g_em->src_y = src_y;
8798 g_em->src_offset_x = 0;
8799 g_em->src_offset_y = 0;
8800 g_em->dst_offset_x = 0;
8801 g_em->dst_offset_y = 0;
8802 g_em->width = TILEX;
8803 g_em->height = TILEY;
8805 g_em->preserve_background = FALSE;
8807 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8810 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8811 effective_action == ACTION_MOVING ||
8812 effective_action == ACTION_PUSHING ||
8813 effective_action == ACTION_EATING)) ||
8814 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8815 effective_action == ACTION_EMPTYING)))
8818 (effective_action == ACTION_FALLING ||
8819 effective_action == ACTION_FILLING ||
8820 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8821 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8822 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8823 int num_steps = (i == Ydrip_s1 ? 16 :
8824 i == Ydrip_s1B ? 16 :
8825 i == Ydrip_s2 ? 16 :
8826 i == Ydrip_s2B ? 16 :
8827 i == Xsand_stonein_1 ? 32 :
8828 i == Xsand_stonein_2 ? 32 :
8829 i == Xsand_stonein_3 ? 32 :
8830 i == Xsand_stonein_4 ? 32 :
8831 i == Xsand_stoneout_1 ? 16 :
8832 i == Xsand_stoneout_2 ? 16 : 8);
8833 int cx = ABS(dx) * (TILEX / num_steps);
8834 int cy = ABS(dy) * (TILEY / num_steps);
8835 int step_frame = (i == Ydrip_s2 ? j + 8 :
8836 i == Ydrip_s2B ? j + 8 :
8837 i == Xsand_stonein_2 ? j + 8 :
8838 i == Xsand_stonein_3 ? j + 16 :
8839 i == Xsand_stonein_4 ? j + 24 :
8840 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8841 int step = (is_backside ? step_frame : num_steps - step_frame);
8843 if (is_backside) // tile where movement starts
8845 if (dx < 0 || dy < 0)
8847 g_em->src_offset_x = cx * step;
8848 g_em->src_offset_y = cy * step;
8852 g_em->dst_offset_x = cx * step;
8853 g_em->dst_offset_y = cy * step;
8856 else // tile where movement ends
8858 if (dx < 0 || dy < 0)
8860 g_em->dst_offset_x = cx * step;
8861 g_em->dst_offset_y = cy * step;
8865 g_em->src_offset_x = cx * step;
8866 g_em->src_offset_y = cy * step;
8870 g_em->width = TILEX - cx * step;
8871 g_em->height = TILEY - cy * step;
8874 // create unique graphic identifier to decide if tile must be redrawn
8875 /* bit 31 - 16 (16 bit): EM style graphic
8876 bit 15 - 12 ( 4 bit): EM style frame
8877 bit 11 - 6 ( 6 bit): graphic width
8878 bit 5 - 0 ( 6 bit): graphic height */
8879 g_em->unique_identifier =
8880 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8884 // skip check for EMC elements not contained in original EMC artwork
8885 if (element == EL_EMC_FAKE_ACID)
8888 if (g_em->bitmap != debug_bitmap ||
8889 g_em->src_x != debug_src_x ||
8890 g_em->src_y != debug_src_y ||
8891 g_em->src_offset_x != 0 ||
8892 g_em->src_offset_y != 0 ||
8893 g_em->dst_offset_x != 0 ||
8894 g_em->dst_offset_y != 0 ||
8895 g_em->width != TILEX ||
8896 g_em->height != TILEY)
8898 static int last_i = -1;
8906 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8907 i, element, element_info[element].token_name,
8908 element_action_info[effective_action].suffix, direction);
8910 if (element != effective_element)
8911 printf(" [%d ('%s')]",
8913 element_info[effective_element].token_name);
8917 if (g_em->bitmap != debug_bitmap)
8918 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8919 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8921 if (g_em->src_x != debug_src_x ||
8922 g_em->src_y != debug_src_y)
8923 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8924 j, (is_backside ? 'B' : 'F'),
8925 g_em->src_x, g_em->src_y,
8926 g_em->src_x / 32, g_em->src_y / 32,
8927 debug_src_x, debug_src_y,
8928 debug_src_x / 32, debug_src_y / 32);
8930 if (g_em->src_offset_x != 0 ||
8931 g_em->src_offset_y != 0 ||
8932 g_em->dst_offset_x != 0 ||
8933 g_em->dst_offset_y != 0)
8934 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8936 g_em->src_offset_x, g_em->src_offset_y,
8937 g_em->dst_offset_x, g_em->dst_offset_y);
8939 if (g_em->width != TILEX ||
8940 g_em->height != TILEY)
8941 printf(" %d (%d): size %d,%d should be %d,%d\n",
8943 g_em->width, g_em->height, TILEX, TILEY);
8945 num_em_gfx_errors++;
8952 for (i = 0; i < TILE_MAX; i++)
8954 for (j = 0; j < 8; j++)
8956 int element = object_mapping[i].element_rnd;
8957 int action = object_mapping[i].action;
8958 int direction = object_mapping[i].direction;
8959 boolean is_backside = object_mapping[i].is_backside;
8960 int graphic_action = el_act_dir2img(element, action, direction);
8961 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8963 if ((action == ACTION_SMASHED_BY_ROCK ||
8964 action == ACTION_SMASHED_BY_SPRING ||
8965 action == ACTION_EATING) &&
8966 graphic_action == graphic_default)
8968 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8969 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8970 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8971 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8974 // no separate animation for "smashed by rock" -- use rock instead
8975 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8976 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8978 g_em->bitmap = g_xx->bitmap;
8979 g_em->src_x = g_xx->src_x;
8980 g_em->src_y = g_xx->src_y;
8981 g_em->src_offset_x = g_xx->src_offset_x;
8982 g_em->src_offset_y = g_xx->src_offset_y;
8983 g_em->dst_offset_x = g_xx->dst_offset_x;
8984 g_em->dst_offset_y = g_xx->dst_offset_y;
8985 g_em->width = g_xx->width;
8986 g_em->height = g_xx->height;
8987 g_em->unique_identifier = g_xx->unique_identifier;
8990 g_em->preserve_background = TRUE;
8995 for (p = 0; p < MAX_PLAYERS; p++)
8997 for (i = 0; i < SPR_MAX; i++)
8999 int element = player_mapping[p][i].element_rnd;
9000 int action = player_mapping[p][i].action;
9001 int direction = player_mapping[p][i].direction;
9003 for (j = 0; j < 8; j++)
9005 int effective_element = element;
9006 int effective_action = action;
9007 int graphic = (direction == MV_NONE ?
9008 el_act2img(effective_element, effective_action) :
9009 el_act_dir2img(effective_element, effective_action,
9011 struct GraphicInfo *g = &graphic_info[graphic];
9012 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
9018 Bitmap *debug_bitmap = g_em->bitmap;
9019 int debug_src_x = g_em->src_x;
9020 int debug_src_y = g_em->src_y;
9023 int frame = getAnimationFrame(g->anim_frames,
9026 g->anim_start_frame,
9029 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9031 g_em->bitmap = src_bitmap;
9032 g_em->src_x = src_x;
9033 g_em->src_y = src_y;
9034 g_em->src_offset_x = 0;
9035 g_em->src_offset_y = 0;
9036 g_em->dst_offset_x = 0;
9037 g_em->dst_offset_y = 0;
9038 g_em->width = TILEX;
9039 g_em->height = TILEY;
9043 // skip check for EMC elements not contained in original EMC artwork
9044 if (element == EL_PLAYER_3 ||
9045 element == EL_PLAYER_4)
9048 if (g_em->bitmap != debug_bitmap ||
9049 g_em->src_x != debug_src_x ||
9050 g_em->src_y != debug_src_y)
9052 static int last_i = -1;
9060 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9061 p, i, element, element_info[element].token_name,
9062 element_action_info[effective_action].suffix, direction);
9064 if (element != effective_element)
9065 printf(" [%d ('%s')]",
9067 element_info[effective_element].token_name);
9071 if (g_em->bitmap != debug_bitmap)
9072 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9073 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9075 if (g_em->src_x != debug_src_x ||
9076 g_em->src_y != debug_src_y)
9077 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9079 g_em->src_x, g_em->src_y,
9080 g_em->src_x / 32, g_em->src_y / 32,
9081 debug_src_x, debug_src_y,
9082 debug_src_x / 32, debug_src_y / 32);
9084 num_em_gfx_errors++;
9094 printf("::: [%d errors found]\n", num_em_gfx_errors);
9100 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9101 boolean any_player_moving,
9102 boolean any_player_snapping,
9103 boolean any_player_dropping)
9105 if (frame == 0 && !any_player_dropping)
9107 if (!local_player->was_waiting)
9109 if (!CheckSaveEngineSnapshotToList())
9112 local_player->was_waiting = TRUE;
9115 else if (any_player_moving || any_player_snapping || any_player_dropping)
9117 local_player->was_waiting = FALSE;
9121 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9122 boolean murphy_is_dropping)
9124 if (murphy_is_waiting)
9126 if (!local_player->was_waiting)
9128 if (!CheckSaveEngineSnapshotToList())
9131 local_player->was_waiting = TRUE;
9136 local_player->was_waiting = FALSE;
9140 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9141 boolean button_released)
9143 if (button_released)
9145 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9146 CheckSaveEngineSnapshotToList();
9148 else if (element_clicked)
9150 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9151 CheckSaveEngineSnapshotToList();
9153 game.snapshot.changed_action = TRUE;
9157 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9158 boolean any_player_moving,
9159 boolean any_player_snapping,
9160 boolean any_player_dropping)
9162 if (tape.single_step && tape.recording && !tape.pausing)
9163 if (frame == 0 && !any_player_dropping)
9164 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9166 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9167 any_player_snapping, any_player_dropping);
9170 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9171 boolean murphy_is_dropping)
9173 boolean murphy_starts_dropping = FALSE;
9176 for (i = 0; i < MAX_PLAYERS; i++)
9177 if (stored_player[i].force_dropping)
9178 murphy_starts_dropping = TRUE;
9180 if (tape.single_step && tape.recording && !tape.pausing)
9181 if (murphy_is_waiting && !murphy_starts_dropping)
9182 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9184 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9187 void CheckSingleStepMode_MM(boolean element_clicked,
9188 boolean button_released)
9190 if (tape.single_step && tape.recording && !tape.pausing)
9191 if (button_released)
9192 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9194 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9197 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9198 int graphic, int sync_frame, int x, int y)
9200 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9202 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9205 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9207 return (IS_NEXT_FRAME(sync_frame, graphic));
9210 int getGraphicInfo_Delay(int graphic)
9212 return graphic_info[graphic].anim_delay;
9215 void PlayMenuSoundExt(int sound)
9217 if (sound == SND_UNDEFINED)
9220 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9221 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9224 if (IS_LOOP_SOUND(sound))
9225 PlaySoundLoop(sound);
9230 void PlayMenuSound(void)
9232 PlayMenuSoundExt(menu.sound[game_status]);
9235 void PlayMenuSoundStereo(int sound, int stereo_position)
9237 if (sound == SND_UNDEFINED)
9240 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9241 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9244 if (IS_LOOP_SOUND(sound))
9245 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9247 PlaySoundStereo(sound, stereo_position);
9250 void PlayMenuSoundIfLoopExt(int sound)
9252 if (sound == SND_UNDEFINED)
9255 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9256 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9259 if (IS_LOOP_SOUND(sound))
9260 PlaySoundLoop(sound);
9263 void PlayMenuSoundIfLoop(void)
9265 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9268 void PlayMenuMusicExt(int music)
9270 if (music == MUS_UNDEFINED)
9273 if (!setup.sound_music)
9276 if (IS_LOOP_MUSIC(music))
9277 PlayMusicLoop(music);
9282 void PlayMenuMusic(void)
9284 char *curr_music = getCurrentlyPlayingMusicFilename();
9285 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9287 if (!strEqual(curr_music, next_music))
9288 PlayMenuMusicExt(menu.music[game_status]);
9291 void PlayMenuSoundsAndMusic(void)
9297 static void FadeMenuSounds(void)
9302 static void FadeMenuMusic(void)
9304 char *curr_music = getCurrentlyPlayingMusicFilename();
9305 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9307 if (!strEqual(curr_music, next_music))
9311 void FadeMenuSoundsAndMusic(void)
9317 void PlaySoundActivating(void)
9320 PlaySound(SND_MENU_ITEM_ACTIVATING);
9324 void PlaySoundSelecting(void)
9327 PlaySound(SND_MENU_ITEM_SELECTING);
9331 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9333 boolean change_fullscreen = (setup.fullscreen !=
9334 video.fullscreen_enabled);
9335 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9336 setup.window_scaling_percent !=
9337 video.window_scaling_percent);
9339 if (change_window_scaling_percent && video.fullscreen_enabled)
9342 if (!change_window_scaling_percent && !video.fullscreen_available)
9345 if (change_window_scaling_percent)
9347 SDLSetWindowScaling(setup.window_scaling_percent);
9351 else if (change_fullscreen)
9353 SDLSetWindowFullscreen(setup.fullscreen);
9355 // set setup value according to successfully changed fullscreen mode
9356 setup.fullscreen = video.fullscreen_enabled;
9361 if (change_fullscreen ||
9362 change_window_scaling_percent)
9364 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9366 // save backbuffer content which gets lost when toggling fullscreen mode
9367 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9369 if (change_window_scaling_percent)
9371 // keep window mode, but change window scaling
9372 video.fullscreen_enabled = TRUE; // force new window scaling
9375 // toggle fullscreen
9376 ChangeVideoModeIfNeeded(setup.fullscreen);
9378 // set setup value according to successfully changed fullscreen mode
9379 setup.fullscreen = video.fullscreen_enabled;
9381 // restore backbuffer content from temporary backbuffer backup bitmap
9382 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9384 FreeBitmap(tmp_backbuffer);
9386 // update visible window/screen
9387 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9391 static void JoinRectangles(int *x, int *y, int *width, int *height,
9392 int x2, int y2, int width2, int height2)
9394 // do not join with "off-screen" rectangle
9395 if (x2 == -1 || y2 == -1)
9400 *width = MAX(*width, width2);
9401 *height = MAX(*height, height2);
9404 void SetAnimStatus(int anim_status_new)
9406 if (anim_status_new == GAME_MODE_MAIN)
9407 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9408 else if (anim_status_new == GAME_MODE_SCORES)
9409 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9411 global.anim_status_next = anim_status_new;
9413 // directly set screen modes that are entered without fading
9414 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9415 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9416 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9417 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9418 global.anim_status = global.anim_status_next;
9421 void SetGameStatus(int game_status_new)
9423 if (game_status_new != game_status)
9424 game_status_last_screen = game_status;
9426 game_status = game_status_new;
9428 SetAnimStatus(game_status_new);
9431 void SetFontStatus(int game_status_new)
9433 static int last_game_status = -1;
9435 if (game_status_new != -1)
9437 // set game status for font use after storing last game status
9438 last_game_status = game_status;
9439 game_status = game_status_new;
9443 // reset game status after font use from last stored game status
9444 game_status = last_game_status;
9448 void ResetFontStatus(void)
9453 void SetLevelSetInfo(char *identifier, int level_nr)
9455 setString(&levelset.identifier, identifier);
9457 levelset.level_nr = level_nr;
9460 boolean CheckIfAllViewportsHaveChanged(void)
9462 // if game status has not changed, viewports have not changed either
9463 if (game_status == game_status_last)
9466 // check if all viewports have changed with current game status
9468 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9469 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9470 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9471 int new_real_sx = vp_playfield->x;
9472 int new_real_sy = vp_playfield->y;
9473 int new_full_sxsize = vp_playfield->width;
9474 int new_full_sysize = vp_playfield->height;
9475 int new_dx = vp_door_1->x;
9476 int new_dy = vp_door_1->y;
9477 int new_dxsize = vp_door_1->width;
9478 int new_dysize = vp_door_1->height;
9479 int new_vx = vp_door_2->x;
9480 int new_vy = vp_door_2->y;
9481 int new_vxsize = vp_door_2->width;
9482 int new_vysize = vp_door_2->height;
9484 boolean playfield_viewport_has_changed =
9485 (new_real_sx != REAL_SX ||
9486 new_real_sy != REAL_SY ||
9487 new_full_sxsize != FULL_SXSIZE ||
9488 new_full_sysize != FULL_SYSIZE);
9490 boolean door_1_viewport_has_changed =
9493 new_dxsize != DXSIZE ||
9494 new_dysize != DYSIZE);
9496 boolean door_2_viewport_has_changed =
9499 new_vxsize != VXSIZE ||
9500 new_vysize != VYSIZE ||
9501 game_status_last == GAME_MODE_EDITOR);
9503 return (playfield_viewport_has_changed &&
9504 door_1_viewport_has_changed &&
9505 door_2_viewport_has_changed);
9508 boolean CheckFadeAll(void)
9510 return (CheckIfGlobalBorderHasChanged() ||
9511 CheckIfAllViewportsHaveChanged());
9514 void ChangeViewportPropertiesIfNeeded(void)
9516 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9517 FALSE : setup.small_game_graphics);
9518 int gfx_game_mode = game_status;
9519 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9521 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9522 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9523 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9524 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9525 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9526 int new_win_xsize = vp_window->width;
9527 int new_win_ysize = vp_window->height;
9528 int border_left = vp_playfield->border_left;
9529 int border_right = vp_playfield->border_right;
9530 int border_top = vp_playfield->border_top;
9531 int border_bottom = vp_playfield->border_bottom;
9532 int new_sx = vp_playfield->x + border_left;
9533 int new_sy = vp_playfield->y + border_top;
9534 int new_sxsize = vp_playfield->width - border_left - border_right;
9535 int new_sysize = vp_playfield->height - border_top - border_bottom;
9536 int new_real_sx = vp_playfield->x;
9537 int new_real_sy = vp_playfield->y;
9538 int new_full_sxsize = vp_playfield->width;
9539 int new_full_sysize = vp_playfield->height;
9540 int new_dx = vp_door_1->x;
9541 int new_dy = vp_door_1->y;
9542 int new_dxsize = vp_door_1->width;
9543 int new_dysize = vp_door_1->height;
9544 int new_vx = vp_door_2->x;
9545 int new_vy = vp_door_2->y;
9546 int new_vxsize = vp_door_2->width;
9547 int new_vysize = vp_door_2->height;
9548 int new_ex = vp_door_3->x;
9549 int new_ey = vp_door_3->y;
9550 int new_exsize = vp_door_3->width;
9551 int new_eysize = vp_door_3->height;
9552 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9553 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9554 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9555 int new_scr_fieldx = new_sxsize / tilesize;
9556 int new_scr_fieldy = new_sysize / tilesize;
9557 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9558 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9559 boolean init_gfx_buffers = FALSE;
9560 boolean init_video_buffer = FALSE;
9561 boolean init_gadgets_and_anims = FALSE;
9562 boolean init_em_graphics = FALSE;
9564 if (new_win_xsize != WIN_XSIZE ||
9565 new_win_ysize != WIN_YSIZE)
9567 WIN_XSIZE = new_win_xsize;
9568 WIN_YSIZE = new_win_ysize;
9570 init_video_buffer = TRUE;
9571 init_gfx_buffers = TRUE;
9572 init_gadgets_and_anims = TRUE;
9574 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9577 if (new_scr_fieldx != SCR_FIELDX ||
9578 new_scr_fieldy != SCR_FIELDY)
9580 // this always toggles between MAIN and GAME when using small tile size
9582 SCR_FIELDX = new_scr_fieldx;
9583 SCR_FIELDY = new_scr_fieldy;
9585 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9596 new_sxsize != SXSIZE ||
9597 new_sysize != SYSIZE ||
9598 new_dxsize != DXSIZE ||
9599 new_dysize != DYSIZE ||
9600 new_vxsize != VXSIZE ||
9601 new_vysize != VYSIZE ||
9602 new_exsize != EXSIZE ||
9603 new_eysize != EYSIZE ||
9604 new_real_sx != REAL_SX ||
9605 new_real_sy != REAL_SY ||
9606 new_full_sxsize != FULL_SXSIZE ||
9607 new_full_sysize != FULL_SYSIZE ||
9608 new_tilesize_var != TILESIZE_VAR
9611 // ------------------------------------------------------------------------
9612 // determine next fading area for changed viewport definitions
9613 // ------------------------------------------------------------------------
9615 // start with current playfield area (default fading area)
9618 FADE_SXSIZE = FULL_SXSIZE;
9619 FADE_SYSIZE = FULL_SYSIZE;
9621 // add new playfield area if position or size has changed
9622 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9623 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9625 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9626 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9629 // add current and new door 1 area if position or size has changed
9630 if (new_dx != DX || new_dy != DY ||
9631 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9633 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9634 DX, DY, DXSIZE, DYSIZE);
9635 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9636 new_dx, new_dy, new_dxsize, new_dysize);
9639 // add current and new door 2 area if position or size has changed
9640 if (new_vx != VX || new_vy != VY ||
9641 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9643 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9644 VX, VY, VXSIZE, VYSIZE);
9645 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9646 new_vx, new_vy, new_vxsize, new_vysize);
9649 // ------------------------------------------------------------------------
9650 // handle changed tile size
9651 // ------------------------------------------------------------------------
9653 if (new_tilesize_var != TILESIZE_VAR)
9655 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9657 // changing tile size invalidates scroll values of engine snapshots
9658 FreeEngineSnapshotSingle();
9660 // changing tile size requires update of graphic mapping for EM engine
9661 init_em_graphics = TRUE;
9672 SXSIZE = new_sxsize;
9673 SYSIZE = new_sysize;
9674 DXSIZE = new_dxsize;
9675 DYSIZE = new_dysize;
9676 VXSIZE = new_vxsize;
9677 VYSIZE = new_vysize;
9678 EXSIZE = new_exsize;
9679 EYSIZE = new_eysize;
9680 REAL_SX = new_real_sx;
9681 REAL_SY = new_real_sy;
9682 FULL_SXSIZE = new_full_sxsize;
9683 FULL_SYSIZE = new_full_sysize;
9684 TILESIZE_VAR = new_tilesize_var;
9686 init_gfx_buffers = TRUE;
9687 init_gadgets_and_anims = TRUE;
9689 // printf("::: viewports: init_gfx_buffers\n");
9690 // printf("::: viewports: init_gadgets_and_anims\n");
9693 if (init_gfx_buffers)
9695 // printf("::: init_gfx_buffers\n");
9697 SCR_FIELDX = new_scr_fieldx_buffers;
9698 SCR_FIELDY = new_scr_fieldy_buffers;
9702 SCR_FIELDX = new_scr_fieldx;
9703 SCR_FIELDY = new_scr_fieldy;
9705 SetDrawDeactivationMask(REDRAW_NONE);
9706 SetDrawBackgroundMask(REDRAW_FIELD);
9709 if (init_video_buffer)
9711 // printf("::: init_video_buffer\n");
9713 FreeAllImageTextures(); // needs old renderer to free the textures
9715 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9716 InitImageTextures();
9719 if (init_gadgets_and_anims)
9721 // printf("::: init_gadgets_and_anims\n");
9724 InitGlobalAnimations();
9727 if (init_em_graphics)
9729 InitGraphicInfo_EM();