1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 // select level set with EMC X11 graphics before activating EM GFX debugging
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 // tool button identifiers
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 // constants for number of doors and door parts
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 // forward declaration for internal use
169 static void UnmapToolButtons(void);
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 int getFieldbufferOffsetX_RND(int dir, int pos)
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (dir & MV_HORIZONTAL ? pos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
221 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
223 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
224 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
226 fx += (dx_var > 0 ? TILEX_VAR : 0);
233 if (full_lev_fieldx <= SCR_FIELDX)
235 if (EVEN(SCR_FIELDX))
236 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
238 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
244 int getFieldbufferOffsetY_RND(int dir, int pos)
246 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
247 int dy = (dir & MV_VERTICAL ? pos : 0);
248 int dy_var = dy * TILESIZE_VAR / TILESIZE;
251 if (EVEN(SCR_FIELDY))
253 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
254 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
256 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
257 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
259 fy += (dy_var > 0 ? TILEY_VAR : 0);
266 if (full_lev_fieldy <= SCR_FIELDY)
268 if (EVEN(SCR_FIELDY))
269 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
271 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
277 static int getLevelFromScreenX_RND(int sx)
279 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
282 int lx = LEVELX((px + dx) / TILESIZE_VAR);
287 static int getLevelFromScreenY_RND(int sy)
289 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
292 int ly = LEVELY((py + dy) / TILESIZE_VAR);
297 static int getLevelFromScreenX_EM(int sx)
299 int level_xsize = level.native_em_level->lev->width;
300 int full_xsize = level_xsize * TILESIZE_VAR;
302 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
304 int fx = getFieldbufferOffsetX_EM();
307 int lx = LEVELX((px + dx) / TILESIZE_VAR);
309 lx = correctLevelPosX_EM(lx);
314 static int getLevelFromScreenY_EM(int sy)
316 int level_ysize = level.native_em_level->lev->height;
317 int full_ysize = level_ysize * TILESIZE_VAR;
319 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
321 int fy = getFieldbufferOffsetY_EM();
324 int ly = LEVELY((py + dy) / TILESIZE_VAR);
326 ly = correctLevelPosY_EM(ly);
331 static int getLevelFromScreenX_SP(int sx)
333 int menBorder = setup.sp_show_border_elements;
334 int level_xsize = level.native_sp_level->width;
335 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
337 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
339 int fx = getFieldbufferOffsetX_SP();
342 int lx = LEVELX((px + dx) / TILESIZE_VAR);
347 static int getLevelFromScreenY_SP(int sy)
349 int menBorder = setup.sp_show_border_elements;
350 int level_ysize = level.native_sp_level->height;
351 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
353 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
355 int fy = getFieldbufferOffsetY_SP();
358 int ly = LEVELY((py + dy) / TILESIZE_VAR);
363 static int getLevelFromScreenX_MM(int sx)
365 int level_xsize = level.native_mm_level->fieldx;
366 int full_xsize = level_xsize * TILESIZE_VAR;
368 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
371 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
376 static int getLevelFromScreenY_MM(int sy)
378 int level_ysize = level.native_mm_level->fieldy;
379 int full_ysize = level_ysize * TILESIZE_VAR;
381 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
384 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
389 int getLevelFromScreenX(int x)
391 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
392 return getLevelFromScreenX_EM(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
394 return getLevelFromScreenX_SP(x);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
396 return getLevelFromScreenX_MM(x);
398 return getLevelFromScreenX_RND(x);
401 int getLevelFromScreenY(int y)
403 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
404 return getLevelFromScreenY_EM(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
406 return getLevelFromScreenY_SP(y);
407 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
408 return getLevelFromScreenY_MM(y);
410 return getLevelFromScreenY_RND(y);
413 void DumpTile(int x, int y)
419 printf_line("-", 79);
420 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
421 printf_line("-", 79);
423 if (!IN_LEV_FIELD(x, y))
425 printf("(not in level field)\n");
431 token_name = element_info[Feld[x][y]].token_name;
433 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
434 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
435 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
436 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
437 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
438 printf(" MovPos: %d\n", MovPos[x][y]);
439 printf(" MovDir: %d\n", MovDir[x][y]);
440 printf(" MovDelay: %d\n", MovDelay[x][y]);
441 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
442 printf(" CustomValue: %d\n", CustomValue[x][y]);
443 printf(" GfxElement: %d\n", GfxElement[x][y]);
444 printf(" GfxAction: %d\n", GfxAction[x][y]);
445 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
446 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
450 void DumpTileFromScreen(int sx, int sy)
452 int lx = getLevelFromScreenX(sx);
453 int ly = getLevelFromScreenY(sy);
458 void SetDrawtoField(int mode)
460 if (mode == DRAW_TO_FIELDBUFFER)
466 BX2 = SCR_FIELDX + 1;
467 BY2 = SCR_FIELDY + 1;
469 drawto_field = fieldbuffer;
471 else // DRAW_TO_BACKBUFFER
477 BX2 = SCR_FIELDX - 1;
478 BY2 = SCR_FIELDY - 1;
480 drawto_field = backbuffer;
484 static void RedrawPlayfield_RND(void)
486 if (game.envelope_active)
489 DrawLevel(REDRAW_ALL);
493 void RedrawPlayfield(void)
495 if (game_status != GAME_MODE_PLAYING)
498 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
499 RedrawPlayfield_EM(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
501 RedrawPlayfield_SP(TRUE);
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
503 RedrawPlayfield_MM();
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
505 RedrawPlayfield_RND();
507 BlitScreenToBitmap(backbuffer);
509 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
513 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
516 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
517 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
519 if (x == -1 && y == -1)
522 if (draw_target == DRAW_TO_SCREEN)
523 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
525 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
528 static void DrawMaskedBorderExt_FIELD(int draw_target)
530 if (global.border_status >= GAME_MODE_MAIN &&
531 global.border_status <= GAME_MODE_PLAYING &&
532 border.draw_masked[global.border_status])
533 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
537 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
539 // when drawing to backbuffer, never draw border over open doors
540 if (draw_target == DRAW_TO_BACKBUFFER &&
541 (GetDoorState() & DOOR_OPEN_1))
544 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
545 (global.border_status != GAME_MODE_EDITOR ||
546 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
547 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
550 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
552 // when drawing to backbuffer, never draw border over open doors
553 if (draw_target == DRAW_TO_BACKBUFFER &&
554 (GetDoorState() & DOOR_OPEN_2))
557 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
558 global.border_status != GAME_MODE_EDITOR)
559 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
562 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
564 // currently not available
567 static void DrawMaskedBorderExt_ALL(int draw_target)
569 DrawMaskedBorderExt_FIELD(draw_target);
570 DrawMaskedBorderExt_DOOR_1(draw_target);
571 DrawMaskedBorderExt_DOOR_2(draw_target);
572 DrawMaskedBorderExt_DOOR_3(draw_target);
575 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
577 // never draw masked screen borders on borderless screens
578 if (global.border_status == GAME_MODE_LOADING ||
579 global.border_status == GAME_MODE_TITLE)
582 if (redraw_mask & REDRAW_ALL)
583 DrawMaskedBorderExt_ALL(draw_target);
586 if (redraw_mask & REDRAW_FIELD)
587 DrawMaskedBorderExt_FIELD(draw_target);
588 if (redraw_mask & REDRAW_DOOR_1)
589 DrawMaskedBorderExt_DOOR_1(draw_target);
590 if (redraw_mask & REDRAW_DOOR_2)
591 DrawMaskedBorderExt_DOOR_2(draw_target);
592 if (redraw_mask & REDRAW_DOOR_3)
593 DrawMaskedBorderExt_DOOR_3(draw_target);
597 void DrawMaskedBorder_FIELD(void)
599 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
602 void DrawMaskedBorder(int redraw_mask)
604 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
607 void DrawMaskedBorderToTarget(int draw_target)
609 if (draw_target == DRAW_TO_BACKBUFFER ||
610 draw_target == DRAW_TO_SCREEN)
612 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
616 int last_border_status = global.border_status;
618 if (draw_target == DRAW_TO_FADE_SOURCE)
620 global.border_status = gfx.fade_border_source_status;
621 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
623 else if (draw_target == DRAW_TO_FADE_TARGET)
625 global.border_status = gfx.fade_border_target_status;
626 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
629 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
631 global.border_status = last_border_status;
632 gfx.masked_border_bitmap_ptr = backbuffer;
636 void DrawTileCursor(int draw_target)
642 int graphic = IMG_GLOBAL_TILE_CURSOR;
644 int tilesize = TILESIZE_VAR;
645 int width = tilesize;
646 int height = tilesize;
648 if (game_status != GAME_MODE_PLAYING)
651 if (!tile_cursor.enabled ||
655 if (tile_cursor.moving)
657 int step = TILESIZE_VAR / 4;
658 int dx = tile_cursor.target_x - tile_cursor.x;
659 int dy = tile_cursor.target_y - tile_cursor.y;
662 tile_cursor.x = tile_cursor.target_x;
664 tile_cursor.x += SIGN(dx) * step;
667 tile_cursor.y = tile_cursor.target_y;
669 tile_cursor.y += SIGN(dy) * step;
671 if (tile_cursor.x == tile_cursor.target_x &&
672 tile_cursor.y == tile_cursor.target_y)
673 tile_cursor.moving = FALSE;
676 dst_x = tile_cursor.x;
677 dst_y = tile_cursor.y;
679 frame = getGraphicAnimationFrame(graphic, -1);
681 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
684 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
685 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
687 if (draw_target == DRAW_TO_SCREEN)
688 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
690 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
694 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
696 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
699 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
701 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
702 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
704 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
707 void BlitScreenToBitmap(Bitmap *target_bitmap)
709 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
710 BlitScreenToBitmap_EM(target_bitmap);
711 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
712 BlitScreenToBitmap_SP(target_bitmap);
713 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
714 BlitScreenToBitmap_MM(target_bitmap);
715 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
716 BlitScreenToBitmap_RND(target_bitmap);
718 redraw_mask |= REDRAW_FIELD;
721 static void DrawFramesPerSecond(void)
724 int font_nr = FONT_TEXT_2;
725 int font_width = getFontWidth(font_nr);
726 int draw_deactivation_mask = GetDrawDeactivationMask();
727 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
729 // draw FPS with leading space (needed if field buffer deactivated)
730 sprintf(text, " %04.1f fps", global.frames_per_second);
732 // override draw deactivation mask (required for invisible warp mode)
733 SetDrawDeactivationMask(REDRAW_NONE);
735 // draw opaque FPS if field buffer deactivated, else draw masked FPS
736 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
737 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
739 // set draw deactivation mask to previous value
740 SetDrawDeactivationMask(draw_deactivation_mask);
742 // force full-screen redraw in this frame
743 redraw_mask = REDRAW_ALL;
747 static void PrintFrameTimeDebugging(void)
749 static unsigned int last_counter = 0;
750 unsigned int counter = Counter();
751 int diff_1 = counter - last_counter;
752 int diff_2 = diff_1 - GAME_FRAME_DELAY;
754 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
755 char diff_bar[2 * diff_2_max + 5];
759 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
761 for (i = 0; i < diff_2_max; i++)
762 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
763 i >= diff_2_max - diff_2_cut ? '-' : ' ');
765 diff_bar[pos++] = '|';
767 for (i = 0; i < diff_2_max; i++)
768 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
770 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
772 diff_bar[pos++] = '\0';
774 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
777 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
780 last_counter = counter;
784 static int unifiedRedrawMask(int mask)
786 if (mask & REDRAW_ALL)
789 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
795 static boolean equalRedrawMasks(int mask_1, int mask_2)
797 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
800 void BackToFront(void)
802 static int last_redraw_mask = REDRAW_NONE;
804 // force screen redraw in every frame to continue drawing global animations
805 // (but always use the last redraw mask to prevent unwanted side effects)
806 if (redraw_mask == REDRAW_NONE)
807 redraw_mask = last_redraw_mask;
809 last_redraw_mask = redraw_mask;
812 // masked border now drawn immediately when blitting backbuffer to window
814 // draw masked border to all viewports, if defined
815 DrawMaskedBorder(redraw_mask);
818 // draw frames per second (only if debug mode is enabled)
819 if (redraw_mask & REDRAW_FPS)
820 DrawFramesPerSecond();
822 // remove playfield redraw before potentially merging with doors redraw
823 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
824 redraw_mask &= ~REDRAW_FIELD;
826 // redraw complete window if both playfield and (some) doors need redraw
827 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
828 redraw_mask = REDRAW_ALL;
830 /* although redrawing the whole window would be fine for normal gameplay,
831 being able to only redraw the playfield is required for deactivating
832 certain drawing areas (mainly playfield) to work, which is needed for
833 warp-forward to be fast enough (by skipping redraw of most frames) */
835 if (redraw_mask & REDRAW_ALL)
837 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
839 else if (redraw_mask & REDRAW_FIELD)
841 BlitBitmap(backbuffer, window,
842 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
844 else if (redraw_mask & REDRAW_DOORS)
846 // merge door areas to prevent calling screen redraw more than once
852 if (redraw_mask & REDRAW_DOOR_1)
856 x2 = MAX(x2, DX + DXSIZE);
857 y2 = MAX(y2, DY + DYSIZE);
860 if (redraw_mask & REDRAW_DOOR_2)
864 x2 = MAX(x2, VX + VXSIZE);
865 y2 = MAX(y2, VY + VYSIZE);
868 if (redraw_mask & REDRAW_DOOR_3)
872 x2 = MAX(x2, EX + EXSIZE);
873 y2 = MAX(y2, EY + EYSIZE);
876 // make sure that at least one pixel is blitted, and inside the screen
877 // (else nothing is blitted, causing the animations not to be updated)
878 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
879 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
880 x2 = MIN(MAX(1, x2), WIN_XSIZE);
881 y2 = MIN(MAX(1, y2), WIN_YSIZE);
883 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
886 redraw_mask = REDRAW_NONE;
889 PrintFrameTimeDebugging();
893 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
895 unsigned int frame_delay_value_old = GetVideoFrameDelay();
897 SetVideoFrameDelay(frame_delay_value);
901 SetVideoFrameDelay(frame_delay_value_old);
904 static int fade_type_skip = FADE_TYPE_NONE;
906 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
908 void (*draw_border_function)(void) = NULL;
909 int x, y, width, height;
910 int fade_delay, post_delay;
912 if (fade_type == FADE_TYPE_FADE_OUT)
914 if (fade_type_skip != FADE_TYPE_NONE)
916 // skip all fade operations until specified fade operation
917 if (fade_type & fade_type_skip)
918 fade_type_skip = FADE_TYPE_NONE;
923 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
927 redraw_mask |= fade_mask;
929 if (fade_type == FADE_TYPE_SKIP)
931 fade_type_skip = fade_mode;
936 fade_delay = fading.fade_delay;
937 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
939 if (fade_type_skip != FADE_TYPE_NONE)
941 // skip all fade operations until specified fade operation
942 if (fade_type & fade_type_skip)
943 fade_type_skip = FADE_TYPE_NONE;
948 if (global.autoplay_leveldir)
953 if (fade_mask == REDRAW_FIELD)
958 height = FADE_SYSIZE;
960 if (border.draw_masked_when_fading)
961 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
963 DrawMaskedBorder_FIELD(); // draw once
973 if (!setup.fade_screens ||
975 fading.fade_mode == FADE_MODE_NONE)
977 if (fade_mode == FADE_MODE_FADE_OUT)
980 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
982 redraw_mask &= ~fade_mask;
987 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
988 draw_border_function);
990 redraw_mask &= ~fade_mask;
992 ClearAutoRepeatKeyEvents();
995 static void SetScreenStates_BeforeFadingIn(void)
997 // temporarily set screen mode for animations to screen after fading in
998 global.anim_status = global.anim_status_next;
1000 // store backbuffer with all animations that will be started after fading in
1001 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
1002 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
1004 // set screen mode for animations back to fading
1005 global.anim_status = GAME_MODE_PSEUDO_FADING;
1008 static void SetScreenStates_AfterFadingIn(void)
1010 // store new source screen (to use correct masked border for fading)
1011 gfx.fade_border_source_status = global.border_status;
1013 global.anim_status = global.anim_status_next;
1016 static void SetScreenStates_BeforeFadingOut(void)
1018 // store new target screen (to use correct masked border for fading)
1019 gfx.fade_border_target_status = game_status;
1021 // set screen mode for animations to fading
1022 global.anim_status = GAME_MODE_PSEUDO_FADING;
1024 // store backbuffer with all animations that will be stopped for fading out
1025 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1026 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1029 static void SetScreenStates_AfterFadingOut(void)
1031 global.border_status = game_status;
1034 void FadeIn(int fade_mask)
1036 SetScreenStates_BeforeFadingIn();
1039 DrawMaskedBorder(REDRAW_ALL);
1042 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1043 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1045 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1049 FADE_SXSIZE = FULL_SXSIZE;
1050 FADE_SYSIZE = FULL_SYSIZE;
1052 // activate virtual buttons depending on upcoming game status
1053 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1054 game_status == GAME_MODE_PLAYING && !tape.playing)
1055 SetOverlayActive(TRUE);
1057 SetScreenStates_AfterFadingIn();
1059 // force update of global animation status in case of rapid screen changes
1060 redraw_mask = REDRAW_ALL;
1064 void FadeOut(int fade_mask)
1066 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1067 if (!equalRedrawMasks(fade_mask, redraw_mask))
1070 SetScreenStates_BeforeFadingOut();
1072 SetTileCursorActive(FALSE);
1073 SetOverlayActive(FALSE);
1076 DrawMaskedBorder(REDRAW_ALL);
1079 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1080 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1082 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1084 SetScreenStates_AfterFadingOut();
1087 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1089 static struct TitleFadingInfo fading_leave_stored;
1092 fading_leave_stored = fading_leave;
1094 fading = fading_leave_stored;
1097 void FadeSetEnterMenu(void)
1099 fading = menu.enter_menu;
1101 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1104 void FadeSetLeaveMenu(void)
1106 fading = menu.leave_menu;
1108 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1111 void FadeSetEnterScreen(void)
1113 fading = menu.enter_screen[game_status];
1115 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1118 void FadeSetNextScreen(void)
1120 fading = menu.next_screen[game_status];
1122 // (do not overwrite fade mode set by FadeSetEnterScreen)
1123 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1126 void FadeSetLeaveScreen(void)
1128 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1131 void FadeSetFromType(int type)
1133 if (type & TYPE_ENTER_SCREEN)
1134 FadeSetEnterScreen();
1135 else if (type & TYPE_ENTER)
1137 else if (type & TYPE_LEAVE)
1141 void FadeSetDisabled(void)
1143 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1145 fading = fading_none;
1148 void FadeSkipNextFadeIn(void)
1150 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1153 void FadeSkipNextFadeOut(void)
1155 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1158 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1160 if (graphic == IMG_UNDEFINED)
1163 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1165 return (graphic_info[graphic].bitmap != NULL || redefined ?
1166 graphic_info[graphic].bitmap :
1167 graphic_info[default_graphic].bitmap);
1170 static Bitmap *getBackgroundBitmap(int graphic)
1172 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1175 static Bitmap *getGlobalBorderBitmap(int graphic)
1177 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1180 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1183 (status == GAME_MODE_MAIN ||
1184 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1185 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1186 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1187 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1190 return getGlobalBorderBitmap(graphic);
1193 void SetWindowBackgroundImageIfDefined(int graphic)
1195 if (graphic_info[graphic].bitmap)
1196 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1199 void SetMainBackgroundImageIfDefined(int graphic)
1201 if (graphic_info[graphic].bitmap)
1202 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1205 void SetDoorBackgroundImageIfDefined(int graphic)
1207 if (graphic_info[graphic].bitmap)
1208 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1211 void SetWindowBackgroundImage(int graphic)
1213 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1216 void SetMainBackgroundImage(int graphic)
1218 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1221 void SetDoorBackgroundImage(int graphic)
1223 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1226 void SetPanelBackground(void)
1228 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1230 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1231 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1233 SetDoorBackgroundBitmap(bitmap_db_panel);
1236 void DrawBackground(int x, int y, int width, int height)
1238 // "drawto" might still point to playfield buffer here (hall of fame)
1239 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1241 if (IN_GFX_FIELD_FULL(x, y))
1242 redraw_mask |= REDRAW_FIELD;
1243 else if (IN_GFX_DOOR_1(x, y))
1244 redraw_mask |= REDRAW_DOOR_1;
1245 else if (IN_GFX_DOOR_2(x, y))
1246 redraw_mask |= REDRAW_DOOR_2;
1247 else if (IN_GFX_DOOR_3(x, y))
1248 redraw_mask |= REDRAW_DOOR_3;
1251 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1253 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1255 if (font->bitmap == NULL)
1258 DrawBackground(x, y, width, height);
1261 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1263 struct GraphicInfo *g = &graphic_info[graphic];
1265 if (g->bitmap == NULL)
1268 DrawBackground(x, y, width, height);
1271 static int game_status_last = -1;
1272 static Bitmap *global_border_bitmap_last = NULL;
1273 static Bitmap *global_border_bitmap = NULL;
1274 static int real_sx_last = -1, real_sy_last = -1;
1275 static int full_sxsize_last = -1, full_sysize_last = -1;
1276 static int dx_last = -1, dy_last = -1;
1277 static int dxsize_last = -1, dysize_last = -1;
1278 static int vx_last = -1, vy_last = -1;
1279 static int vxsize_last = -1, vysize_last = -1;
1280 static int ex_last = -1, ey_last = -1;
1281 static int exsize_last = -1, eysize_last = -1;
1283 boolean CheckIfGlobalBorderHasChanged(void)
1285 // if game status has not changed, global border has not changed either
1286 if (game_status == game_status_last)
1289 // determine and store new global border bitmap for current game status
1290 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1292 return (global_border_bitmap_last != global_border_bitmap);
1295 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1297 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1298 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1300 // if game status has not changed, nothing has to be redrawn
1301 if (game_status == game_status_last)
1304 // redraw if last screen was title screen
1305 if (game_status_last == GAME_MODE_TITLE)
1308 // redraw if global screen border has changed
1309 if (CheckIfGlobalBorderHasChanged())
1312 // redraw if position or size of playfield area has changed
1313 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1314 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1317 // redraw if position or size of door area has changed
1318 if (dx_last != DX || dy_last != DY ||
1319 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1322 // redraw if position or size of tape area has changed
1323 if (vx_last != VX || vy_last != VY ||
1324 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1327 // redraw if position or size of editor area has changed
1328 if (ex_last != EX || ey_last != EY ||
1329 exsize_last != EXSIZE || eysize_last != EYSIZE)
1336 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1339 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1341 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1344 void RedrawGlobalBorder(void)
1346 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1348 RedrawGlobalBorderFromBitmap(bitmap);
1350 redraw_mask = REDRAW_ALL;
1353 static void RedrawGlobalBorderIfNeeded(void)
1355 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1356 if (game_status == game_status_last)
1360 // copy current draw buffer to later copy back areas that have not changed
1361 if (game_status_last != GAME_MODE_TITLE)
1362 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1364 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1365 if (CheckIfGlobalBorderRedrawIsNeeded())
1368 // redraw global screen border (or clear, if defined to be empty)
1369 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1371 if (game_status == GAME_MODE_EDITOR)
1372 DrawSpecialEditorDoor();
1374 // copy previous playfield and door areas, if they are defined on both
1375 // previous and current screen and if they still have the same size
1377 if (real_sx_last != -1 && real_sy_last != -1 &&
1378 REAL_SX != -1 && REAL_SY != -1 &&
1379 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1380 BlitBitmap(bitmap_db_store_1, backbuffer,
1381 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1384 if (dx_last != -1 && dy_last != -1 &&
1385 DX != -1 && DY != -1 &&
1386 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1387 BlitBitmap(bitmap_db_store_1, backbuffer,
1388 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1390 if (game_status != GAME_MODE_EDITOR)
1392 if (vx_last != -1 && vy_last != -1 &&
1393 VX != -1 && VY != -1 &&
1394 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1395 BlitBitmap(bitmap_db_store_1, backbuffer,
1396 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1400 if (ex_last != -1 && ey_last != -1 &&
1401 EX != -1 && EY != -1 &&
1402 exsize_last == EXSIZE && eysize_last == EYSIZE)
1403 BlitBitmap(bitmap_db_store_1, backbuffer,
1404 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1407 redraw_mask = REDRAW_ALL;
1410 game_status_last = game_status;
1412 global_border_bitmap_last = global_border_bitmap;
1414 real_sx_last = REAL_SX;
1415 real_sy_last = REAL_SY;
1416 full_sxsize_last = FULL_SXSIZE;
1417 full_sysize_last = FULL_SYSIZE;
1420 dxsize_last = DXSIZE;
1421 dysize_last = DYSIZE;
1424 vxsize_last = VXSIZE;
1425 vysize_last = VYSIZE;
1428 exsize_last = EXSIZE;
1429 eysize_last = EYSIZE;
1432 void ClearField(void)
1434 RedrawGlobalBorderIfNeeded();
1436 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1437 // (when entering hall of fame after playing)
1438 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1440 // !!! maybe this should be done before clearing the background !!!
1441 if (game_status == GAME_MODE_PLAYING)
1443 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1444 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1448 SetDrawtoField(DRAW_TO_BACKBUFFER);
1452 void MarkTileDirty(int x, int y)
1454 redraw_mask |= REDRAW_FIELD;
1457 void SetBorderElement(void)
1461 BorderElement = EL_EMPTY;
1463 // the MM game engine does not use a visible border element
1464 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1467 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1469 for (x = 0; x < lev_fieldx; x++)
1471 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1472 BorderElement = EL_STEELWALL;
1474 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1480 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1481 int max_array_fieldx, int max_array_fieldy,
1482 short field[max_array_fieldx][max_array_fieldy],
1483 int max_fieldx, int max_fieldy)
1487 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1488 static int safety = 0;
1490 // check if starting field still has the desired content
1491 if (field[from_x][from_y] == fill_element)
1496 if (safety > max_fieldx * max_fieldy)
1497 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1499 old_element = field[from_x][from_y];
1500 field[from_x][from_y] = fill_element;
1502 for (i = 0; i < 4; i++)
1504 x = from_x + check[i][0];
1505 y = from_y + check[i][1];
1507 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1508 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1509 field, max_fieldx, max_fieldy);
1515 void FloodFillLevel(int from_x, int from_y, int fill_element,
1516 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1517 int max_fieldx, int max_fieldy)
1519 FloodFillLevelExt(from_x, from_y, fill_element,
1520 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1521 max_fieldx, max_fieldy);
1524 void SetRandomAnimationValue(int x, int y)
1526 gfx.anim_random_frame = GfxRandom[x][y];
1529 int getGraphicAnimationFrame(int graphic, int sync_frame)
1531 // animation synchronized with global frame counter, not move position
1532 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1533 sync_frame = FrameCounter;
1535 return getAnimationFrame(graphic_info[graphic].anim_frames,
1536 graphic_info[graphic].anim_delay,
1537 graphic_info[graphic].anim_mode,
1538 graphic_info[graphic].anim_start_frame,
1542 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1544 struct GraphicInfo *g = &graphic_info[graphic];
1545 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1547 if (tilesize == gfx.standard_tile_size)
1548 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1549 else if (tilesize == game.tile_size)
1550 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1552 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1555 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1556 boolean get_backside)
1558 struct GraphicInfo *g = &graphic_info[graphic];
1559 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1560 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1562 if (g->offset_y == 0) // frames are ordered horizontally
1564 int max_width = g->anim_frames_per_line * g->width;
1565 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1567 *x = pos % max_width;
1568 *y = src_y % g->height + pos / max_width * g->height;
1570 else if (g->offset_x == 0) // frames are ordered vertically
1572 int max_height = g->anim_frames_per_line * g->height;
1573 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1575 *x = src_x % g->width + pos / max_height * g->width;
1576 *y = pos % max_height;
1578 else // frames are ordered diagonally
1580 *x = src_x + frame * g->offset_x;
1581 *y = src_y + frame * g->offset_y;
1585 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1586 Bitmap **bitmap, int *x, int *y,
1587 boolean get_backside)
1589 struct GraphicInfo *g = &graphic_info[graphic];
1591 // if no graphics defined at all, use fallback graphics
1592 if (g->bitmaps == NULL)
1593 *g = graphic_info[IMG_CHAR_EXCLAM];
1595 // if no in-game graphics defined, always use standard graphic size
1596 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1597 tilesize = TILESIZE;
1599 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1600 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1602 *x = *x * tilesize / g->tile_size;
1603 *y = *y * tilesize / g->tile_size;
1606 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1607 Bitmap **bitmap, int *x, int *y)
1609 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1612 void getFixedGraphicSource(int graphic, int frame,
1613 Bitmap **bitmap, int *x, int *y)
1615 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1618 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1620 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1623 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1624 int *x, int *y, boolean get_backside)
1626 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1630 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1632 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1635 void DrawGraphic(int x, int y, int graphic, int frame)
1638 if (!IN_SCR_FIELD(x, y))
1640 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1641 printf("DrawGraphic(): This should never happen!\n");
1646 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1649 MarkTileDirty(x, y);
1652 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1655 if (!IN_SCR_FIELD(x, y))
1657 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1658 printf("DrawGraphic(): This should never happen!\n");
1663 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1665 MarkTileDirty(x, y);
1668 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1674 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1676 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1679 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1685 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1686 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1689 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1692 if (!IN_SCR_FIELD(x, y))
1694 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1695 printf("DrawGraphicThruMask(): This should never happen!\n");
1700 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1703 MarkTileDirty(x, y);
1706 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1709 if (!IN_SCR_FIELD(x, y))
1711 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1712 printf("DrawGraphicThruMask(): This should never happen!\n");
1717 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1719 MarkTileDirty(x, y);
1722 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1728 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1730 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1734 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1735 int graphic, int frame)
1740 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1742 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1746 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1748 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1750 MarkTileDirty(x / tilesize, y / tilesize);
1753 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1756 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1757 graphic, frame, tilesize);
1758 MarkTileDirty(x / tilesize, y / tilesize);
1761 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1767 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1768 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1771 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1772 int frame, int tilesize)
1777 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1778 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1781 void DrawMiniGraphic(int x, int y, int graphic)
1783 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1784 MarkTileDirty(x / 2, y / 2);
1787 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1792 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1793 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1796 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1797 int graphic, int frame,
1798 int cut_mode, int mask_mode)
1803 int width = TILEX, height = TILEY;
1806 if (dx || dy) // shifted graphic
1808 if (x < BX1) // object enters playfield from the left
1815 else if (x > BX2) // object enters playfield from the right
1821 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1827 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1829 else if (dx) // general horizontal movement
1830 MarkTileDirty(x + SIGN(dx), y);
1832 if (y < BY1) // object enters playfield from the top
1834 if (cut_mode == CUT_BELOW) // object completely above top border
1842 else if (y > BY2) // object enters playfield from the bottom
1848 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1854 else if (dy > 0 && cut_mode == CUT_ABOVE)
1856 if (y == BY2) // object completely above bottom border
1862 MarkTileDirty(x, y + 1);
1863 } // object leaves playfield to the bottom
1864 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1866 else if (dy) // general vertical movement
1867 MarkTileDirty(x, y + SIGN(dy));
1871 if (!IN_SCR_FIELD(x, y))
1873 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1874 printf("DrawGraphicShifted(): This should never happen!\n");
1879 width = width * TILESIZE_VAR / TILESIZE;
1880 height = height * TILESIZE_VAR / TILESIZE;
1881 cx = cx * TILESIZE_VAR / TILESIZE;
1882 cy = cy * TILESIZE_VAR / TILESIZE;
1883 dx = dx * TILESIZE_VAR / TILESIZE;
1884 dy = dy * TILESIZE_VAR / TILESIZE;
1886 if (width > 0 && height > 0)
1888 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1893 dst_x = FX + x * TILEX_VAR + dx;
1894 dst_y = FY + y * TILEY_VAR + dy;
1896 if (mask_mode == USE_MASKING)
1897 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1900 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1903 MarkTileDirty(x, y);
1907 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1908 int graphic, int frame,
1909 int cut_mode, int mask_mode)
1914 int width = TILEX_VAR, height = TILEY_VAR;
1917 int x2 = x + SIGN(dx);
1918 int y2 = y + SIGN(dy);
1920 // movement with two-tile animations must be sync'ed with movement position,
1921 // not with current GfxFrame (which can be higher when using slow movement)
1922 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1923 int anim_frames = graphic_info[graphic].anim_frames;
1925 // (we also need anim_delay here for movement animations with less frames)
1926 int anim_delay = graphic_info[graphic].anim_delay;
1927 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1929 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1930 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1932 // re-calculate animation frame for two-tile movement animation
1933 frame = getGraphicAnimationFrame(graphic, sync_frame);
1935 // check if movement start graphic inside screen area and should be drawn
1936 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1938 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1940 dst_x = FX + x1 * TILEX_VAR;
1941 dst_y = FY + y1 * TILEY_VAR;
1943 if (mask_mode == USE_MASKING)
1944 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1947 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1950 MarkTileDirty(x1, y1);
1953 // check if movement end graphic inside screen area and should be drawn
1954 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1956 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1958 dst_x = FX + x2 * TILEX_VAR;
1959 dst_y = FY + y2 * TILEY_VAR;
1961 if (mask_mode == USE_MASKING)
1962 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1965 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1968 MarkTileDirty(x2, y2);
1972 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1973 int graphic, int frame,
1974 int cut_mode, int mask_mode)
1978 DrawGraphic(x, y, graphic, frame);
1983 if (graphic_info[graphic].double_movement) // EM style movement images
1984 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1986 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1989 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1990 int graphic, int frame, int cut_mode)
1992 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1995 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1996 int cut_mode, int mask_mode)
1998 int lx = LEVELX(x), ly = LEVELY(y);
2002 if (IN_LEV_FIELD(lx, ly))
2004 SetRandomAnimationValue(lx, ly);
2006 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2007 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2009 // do not use double (EM style) movement graphic when not moving
2010 if (graphic_info[graphic].double_movement && !dx && !dy)
2012 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2013 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2016 else // border element
2018 graphic = el2img(element);
2019 frame = getGraphicAnimationFrame(graphic, -1);
2022 if (element == EL_EXPANDABLE_WALL)
2024 boolean left_stopped = FALSE, right_stopped = FALSE;
2026 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2027 left_stopped = TRUE;
2028 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2029 right_stopped = TRUE;
2031 if (left_stopped && right_stopped)
2033 else if (left_stopped)
2035 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2036 frame = graphic_info[graphic].anim_frames - 1;
2038 else if (right_stopped)
2040 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2041 frame = graphic_info[graphic].anim_frames - 1;
2046 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2047 else if (mask_mode == USE_MASKING)
2048 DrawGraphicThruMask(x, y, graphic, frame);
2050 DrawGraphic(x, y, graphic, frame);
2053 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2054 int cut_mode, int mask_mode)
2056 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2057 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2058 cut_mode, mask_mode);
2061 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2064 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2067 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2070 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2073 void DrawLevelElementThruMask(int x, int y, int element)
2075 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2078 void DrawLevelFieldThruMask(int x, int y)
2080 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2083 // !!! implementation of quicksand is totally broken !!!
2084 #define IS_CRUMBLED_TILE(x, y, e) \
2085 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2086 !IS_MOVING(x, y) || \
2087 (e) == EL_QUICKSAND_EMPTYING || \
2088 (e) == EL_QUICKSAND_FAST_EMPTYING))
2090 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2095 int width, height, cx, cy;
2096 int sx = SCREENX(x), sy = SCREENY(y);
2097 int crumbled_border_size = graphic_info[graphic].border_size;
2098 int crumbled_tile_size = graphic_info[graphic].tile_size;
2099 int crumbled_border_size_var =
2100 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2103 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2105 for (i = 1; i < 4; i++)
2107 int dxx = (i & 1 ? dx : 0);
2108 int dyy = (i & 2 ? dy : 0);
2111 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2114 // check if neighbour field is of same crumble type
2115 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2116 graphic_info[graphic].class ==
2117 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2119 // return if check prevents inner corner
2120 if (same == (dxx == dx && dyy == dy))
2124 // if we reach this point, we have an inner corner
2126 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2128 width = crumbled_border_size_var;
2129 height = crumbled_border_size_var;
2130 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2131 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2133 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2134 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2137 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2142 int width, height, bx, by, cx, cy;
2143 int sx = SCREENX(x), sy = SCREENY(y);
2144 int crumbled_border_size = graphic_info[graphic].border_size;
2145 int crumbled_tile_size = graphic_info[graphic].tile_size;
2146 int crumbled_border_size_var =
2147 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2148 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2151 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2153 // draw simple, sloppy, non-corner-accurate crumbled border
2155 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2156 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2157 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2158 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2160 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2161 FX + sx * TILEX_VAR + cx,
2162 FY + sy * TILEY_VAR + cy);
2164 // (remaining middle border part must be at least as big as corner part)
2165 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2166 crumbled_border_size_var >= TILESIZE_VAR / 3)
2169 // correct corners of crumbled border, if needed
2171 for (i = -1; i <= 1; i += 2)
2173 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2174 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2175 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2178 // check if neighbour field is of same crumble type
2179 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2180 graphic_info[graphic].class ==
2181 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2183 // no crumbled corner, but continued crumbled border
2185 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2186 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2187 int b1 = (i == 1 ? crumbled_border_size_var :
2188 TILESIZE_VAR - 2 * crumbled_border_size_var);
2190 width = crumbled_border_size_var;
2191 height = crumbled_border_size_var;
2193 if (dir == 1 || dir == 2)
2208 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2210 FX + sx * TILEX_VAR + cx,
2211 FY + sy * TILEY_VAR + cy);
2216 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2218 int sx = SCREENX(x), sy = SCREENY(y);
2221 static int xy[4][2] =
2229 if (!IN_LEV_FIELD(x, y))
2232 element = TILE_GFX_ELEMENT(x, y);
2234 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2236 if (!IN_SCR_FIELD(sx, sy))
2239 // crumble field borders towards direct neighbour fields
2240 for (i = 0; i < 4; i++)
2242 int xx = x + xy[i][0];
2243 int yy = y + xy[i][1];
2245 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2248 // check if neighbour field is of same crumble type
2249 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2250 graphic_info[graphic].class ==
2251 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2254 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2257 // crumble inner field corners towards corner neighbour fields
2258 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2259 graphic_info[graphic].anim_frames == 2)
2261 for (i = 0; i < 4; i++)
2263 int dx = (i & 1 ? +1 : -1);
2264 int dy = (i & 2 ? +1 : -1);
2266 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2270 MarkTileDirty(sx, sy);
2272 else // center field is not crumbled -- crumble neighbour fields
2274 // crumble field borders of direct neighbour fields
2275 for (i = 0; i < 4; i++)
2277 int xx = x + xy[i][0];
2278 int yy = y + xy[i][1];
2279 int sxx = sx + xy[i][0];
2280 int syy = sy + xy[i][1];
2282 if (!IN_LEV_FIELD(xx, yy) ||
2283 !IN_SCR_FIELD(sxx, syy))
2286 // do not crumble fields that are being digged or snapped
2287 if (Feld[xx][yy] == EL_EMPTY ||
2288 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2291 element = TILE_GFX_ELEMENT(xx, yy);
2293 if (!IS_CRUMBLED_TILE(xx, yy, element))
2296 graphic = el_act2crm(element, ACTION_DEFAULT);
2298 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2300 MarkTileDirty(sxx, syy);
2303 // crumble inner field corners of corner neighbour fields
2304 for (i = 0; i < 4; i++)
2306 int dx = (i & 1 ? +1 : -1);
2307 int dy = (i & 2 ? +1 : -1);
2313 if (!IN_LEV_FIELD(xx, yy) ||
2314 !IN_SCR_FIELD(sxx, syy))
2317 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2320 element = TILE_GFX_ELEMENT(xx, yy);
2322 if (!IS_CRUMBLED_TILE(xx, yy, element))
2325 graphic = el_act2crm(element, ACTION_DEFAULT);
2327 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2328 graphic_info[graphic].anim_frames == 2)
2329 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2331 MarkTileDirty(sxx, syy);
2336 void DrawLevelFieldCrumbled(int x, int y)
2340 if (!IN_LEV_FIELD(x, y))
2343 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2344 GfxElement[x][y] != EL_UNDEFINED &&
2345 GFX_CRUMBLED(GfxElement[x][y]))
2347 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2352 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2354 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2357 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2360 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2361 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2362 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2363 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2364 int sx = SCREENX(x), sy = SCREENY(y);
2366 DrawGraphic(sx, sy, graphic1, frame1);
2367 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2370 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2372 int sx = SCREENX(x), sy = SCREENY(y);
2373 static int xy[4][2] =
2382 // crumble direct neighbour fields (required for field borders)
2383 for (i = 0; i < 4; i++)
2385 int xx = x + xy[i][0];
2386 int yy = y + xy[i][1];
2387 int sxx = sx + xy[i][0];
2388 int syy = sy + xy[i][1];
2390 if (!IN_LEV_FIELD(xx, yy) ||
2391 !IN_SCR_FIELD(sxx, syy) ||
2392 !GFX_CRUMBLED(Feld[xx][yy]) ||
2396 DrawLevelField(xx, yy);
2399 // crumble corner neighbour fields (required for inner field corners)
2400 for (i = 0; i < 4; i++)
2402 int dx = (i & 1 ? +1 : -1);
2403 int dy = (i & 2 ? +1 : -1);
2409 if (!IN_LEV_FIELD(xx, yy) ||
2410 !IN_SCR_FIELD(sxx, syy) ||
2411 !GFX_CRUMBLED(Feld[xx][yy]) ||
2415 int element = TILE_GFX_ELEMENT(xx, yy);
2416 int graphic = el_act2crm(element, ACTION_DEFAULT);
2418 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2419 graphic_info[graphic].anim_frames == 2)
2420 DrawLevelField(xx, yy);
2424 static int getBorderElement(int x, int y)
2428 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2429 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2430 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2431 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2432 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2433 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2434 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2436 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2437 int steel_position = (x == -1 && y == -1 ? 0 :
2438 x == lev_fieldx && y == -1 ? 1 :
2439 x == -1 && y == lev_fieldy ? 2 :
2440 x == lev_fieldx && y == lev_fieldy ? 3 :
2441 x == -1 || x == lev_fieldx ? 4 :
2442 y == -1 || y == lev_fieldy ? 5 : 6);
2444 return border[steel_position][steel_type];
2447 void DrawScreenElement(int x, int y, int element)
2449 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2450 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2453 void DrawLevelElement(int x, int y, int element)
2455 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2456 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2459 void DrawScreenField(int x, int y)
2461 int lx = LEVELX(x), ly = LEVELY(y);
2462 int element, content;
2464 if (!IN_LEV_FIELD(lx, ly))
2466 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2469 element = getBorderElement(lx, ly);
2471 DrawScreenElement(x, y, element);
2476 element = Feld[lx][ly];
2477 content = Store[lx][ly];
2479 if (IS_MOVING(lx, ly))
2481 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2482 boolean cut_mode = NO_CUTTING;
2484 if (element == EL_QUICKSAND_EMPTYING ||
2485 element == EL_QUICKSAND_FAST_EMPTYING ||
2486 element == EL_MAGIC_WALL_EMPTYING ||
2487 element == EL_BD_MAGIC_WALL_EMPTYING ||
2488 element == EL_DC_MAGIC_WALL_EMPTYING ||
2489 element == EL_AMOEBA_DROPPING)
2490 cut_mode = CUT_ABOVE;
2491 else if (element == EL_QUICKSAND_FILLING ||
2492 element == EL_QUICKSAND_FAST_FILLING ||
2493 element == EL_MAGIC_WALL_FILLING ||
2494 element == EL_BD_MAGIC_WALL_FILLING ||
2495 element == EL_DC_MAGIC_WALL_FILLING)
2496 cut_mode = CUT_BELOW;
2498 if (cut_mode == CUT_ABOVE)
2499 DrawScreenElement(x, y, element);
2501 DrawScreenElement(x, y, EL_EMPTY);
2504 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2505 else if (cut_mode == NO_CUTTING)
2506 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2509 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2511 if (cut_mode == CUT_BELOW &&
2512 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2513 DrawLevelElement(lx, ly + 1, element);
2516 if (content == EL_ACID)
2518 int dir = MovDir[lx][ly];
2519 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2520 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2522 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2524 // prevent target field from being drawn again (but without masking)
2525 // (this would happen if target field is scanned after moving element)
2526 Stop[newlx][newly] = TRUE;
2529 else if (IS_BLOCKED(lx, ly))
2534 boolean cut_mode = NO_CUTTING;
2535 int element_old, content_old;
2537 Blocked2Moving(lx, ly, &oldx, &oldy);
2540 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2541 MovDir[oldx][oldy] == MV_RIGHT);
2543 element_old = Feld[oldx][oldy];
2544 content_old = Store[oldx][oldy];
2546 if (element_old == EL_QUICKSAND_EMPTYING ||
2547 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2548 element_old == EL_MAGIC_WALL_EMPTYING ||
2549 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2550 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2551 element_old == EL_AMOEBA_DROPPING)
2552 cut_mode = CUT_ABOVE;
2554 DrawScreenElement(x, y, EL_EMPTY);
2557 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2559 else if (cut_mode == NO_CUTTING)
2560 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2563 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2566 else if (IS_DRAWABLE(element))
2567 DrawScreenElement(x, y, element);
2569 DrawScreenElement(x, y, EL_EMPTY);
2572 void DrawLevelField(int x, int y)
2574 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2575 DrawScreenField(SCREENX(x), SCREENY(y));
2576 else if (IS_MOVING(x, y))
2580 Moving2Blocked(x, y, &newx, &newy);
2581 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2582 DrawScreenField(SCREENX(newx), SCREENY(newy));
2584 else if (IS_BLOCKED(x, y))
2588 Blocked2Moving(x, y, &oldx, &oldy);
2589 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2590 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2594 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2595 int (*el2img_function)(int), boolean masked,
2596 int element_bits_draw)
2598 int element_base = map_mm_wall_element(element);
2599 int element_bits = (IS_DF_WALL(element) ?
2600 element - EL_DF_WALL_START :
2601 IS_MM_WALL(element) ?
2602 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2603 int graphic = el2img_function(element_base);
2604 int tilesize_draw = tilesize / 2;
2609 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2611 for (i = 0; i < 4; i++)
2613 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2614 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2616 if (!(element_bits_draw & (1 << i)))
2619 if (element_bits & (1 << i))
2622 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2623 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2625 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2626 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2631 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2632 tilesize_draw, tilesize_draw);
2637 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2638 boolean masked, int element_bits_draw)
2640 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2641 element, tilesize, el2edimg, masked, element_bits_draw);
2644 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2645 int (*el2img_function)(int))
2647 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2651 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2654 if (IS_MM_WALL(element))
2656 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2657 element, tilesize, el2edimg, masked, 0x000f);
2661 int graphic = el2edimg(element);
2664 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2666 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2670 void DrawSizedElement(int x, int y, int element, int tilesize)
2672 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2675 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2677 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2680 void DrawMiniElement(int x, int y, int element)
2684 graphic = el2edimg(element);
2685 DrawMiniGraphic(x, y, graphic);
2688 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2691 int x = sx + scroll_x, y = sy + scroll_y;
2693 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2694 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2695 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2696 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2698 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2701 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2703 int x = sx + scroll_x, y = sy + scroll_y;
2705 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2706 DrawMiniElement(sx, sy, EL_EMPTY);
2707 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2708 DrawMiniElement(sx, sy, Feld[x][y]);
2710 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2713 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2714 int x, int y, int xsize, int ysize,
2715 int tile_width, int tile_height)
2719 int dst_x = startx + x * tile_width;
2720 int dst_y = starty + y * tile_height;
2721 int width = graphic_info[graphic].width;
2722 int height = graphic_info[graphic].height;
2723 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2724 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2725 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2726 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2727 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2728 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2729 boolean draw_masked = graphic_info[graphic].draw_masked;
2731 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2733 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2735 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2739 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2740 inner_sx + (x - 1) * tile_width % inner_width);
2741 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2742 inner_sy + (y - 1) * tile_height % inner_height);
2745 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2748 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2752 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2753 int x, int y, int xsize, int ysize,
2756 int font_width = getFontWidth(font_nr);
2757 int font_height = getFontHeight(font_nr);
2759 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2760 font_width, font_height);
2763 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2765 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2766 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2767 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2768 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2769 boolean no_delay = (tape.warp_forward);
2770 unsigned int anim_delay = 0;
2771 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2772 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2773 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2774 int font_width = getFontWidth(font_nr);
2775 int font_height = getFontHeight(font_nr);
2776 int max_xsize = level.envelope[envelope_nr].xsize;
2777 int max_ysize = level.envelope[envelope_nr].ysize;
2778 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2779 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2780 int xend = max_xsize;
2781 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2782 int xstep = (xstart < xend ? 1 : 0);
2783 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2785 int end = MAX(xend - xstart, yend - ystart);
2788 for (i = start; i <= end; i++)
2790 int last_frame = end; // last frame of this "for" loop
2791 int x = xstart + i * xstep;
2792 int y = ystart + i * ystep;
2793 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2794 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2795 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2796 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2799 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2801 BlitScreenToBitmap(backbuffer);
2803 SetDrawtoField(DRAW_TO_BACKBUFFER);
2805 for (yy = 0; yy < ysize; yy++)
2806 for (xx = 0; xx < xsize; xx++)
2807 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2809 DrawTextBuffer(sx + font_width, sy + font_height,
2810 level.envelope[envelope_nr].text, font_nr, max_xsize,
2811 xsize - 2, ysize - 2, 0, mask_mode,
2812 level.envelope[envelope_nr].autowrap,
2813 level.envelope[envelope_nr].centered, FALSE);
2815 redraw_mask |= REDRAW_FIELD;
2818 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2821 ClearAutoRepeatKeyEvents();
2824 void ShowEnvelope(int envelope_nr)
2826 int element = EL_ENVELOPE_1 + envelope_nr;
2827 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2828 int sound_opening = element_info[element].sound[ACTION_OPENING];
2829 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2830 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2831 boolean no_delay = (tape.warp_forward);
2832 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2833 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2834 int anim_mode = graphic_info[graphic].anim_mode;
2835 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2836 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2838 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2840 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2842 if (anim_mode == ANIM_DEFAULT)
2843 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2845 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2848 Delay(wait_delay_value);
2850 WaitForEventToContinue();
2852 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2854 if (anim_mode != ANIM_NONE)
2855 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2857 if (anim_mode == ANIM_DEFAULT)
2858 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2860 game.envelope_active = FALSE;
2862 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2864 redraw_mask |= REDRAW_FIELD;
2868 static void setRequestBasePosition(int *x, int *y)
2870 int sx_base, sy_base;
2872 if (request.x != -1)
2873 sx_base = request.x;
2874 else if (request.align == ALIGN_LEFT)
2876 else if (request.align == ALIGN_RIGHT)
2877 sx_base = SX + SXSIZE;
2879 sx_base = SX + SXSIZE / 2;
2881 if (request.y != -1)
2882 sy_base = request.y;
2883 else if (request.valign == VALIGN_TOP)
2885 else if (request.valign == VALIGN_BOTTOM)
2886 sy_base = SY + SYSIZE;
2888 sy_base = SY + SYSIZE / 2;
2894 static void setRequestPositionExt(int *x, int *y, int width, int height,
2895 boolean add_border_size)
2897 int border_size = request.border_size;
2898 int sx_base, sy_base;
2901 setRequestBasePosition(&sx_base, &sy_base);
2903 if (request.align == ALIGN_LEFT)
2905 else if (request.align == ALIGN_RIGHT)
2906 sx = sx_base - width;
2908 sx = sx_base - width / 2;
2910 if (request.valign == VALIGN_TOP)
2912 else if (request.valign == VALIGN_BOTTOM)
2913 sy = sy_base - height;
2915 sy = sy_base - height / 2;
2917 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2918 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2920 if (add_border_size)
2930 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2932 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2935 static void DrawEnvelopeRequest(char *text)
2937 char *text_final = text;
2938 char *text_door_style = NULL;
2939 int graphic = IMG_BACKGROUND_REQUEST;
2940 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2941 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2942 int font_nr = FONT_REQUEST;
2943 int font_width = getFontWidth(font_nr);
2944 int font_height = getFontHeight(font_nr);
2945 int border_size = request.border_size;
2946 int line_spacing = request.line_spacing;
2947 int line_height = font_height + line_spacing;
2948 int max_text_width = request.width - 2 * border_size;
2949 int max_text_height = request.height - 2 * border_size;
2950 int line_length = max_text_width / font_width;
2951 int max_lines = max_text_height / line_height;
2952 int text_width = line_length * font_width;
2953 int width = request.width;
2954 int height = request.height;
2955 int tile_size = MAX(request.step_offset, 1);
2956 int x_steps = width / tile_size;
2957 int y_steps = height / tile_size;
2958 int sx_offset = border_size;
2959 int sy_offset = border_size;
2963 if (request.centered)
2964 sx_offset = (request.width - text_width) / 2;
2966 if (request.wrap_single_words && !request.autowrap)
2968 char *src_text_ptr, *dst_text_ptr;
2970 text_door_style = checked_malloc(2 * strlen(text) + 1);
2972 src_text_ptr = text;
2973 dst_text_ptr = text_door_style;
2975 while (*src_text_ptr)
2977 if (*src_text_ptr == ' ' ||
2978 *src_text_ptr == '?' ||
2979 *src_text_ptr == '!')
2980 *dst_text_ptr++ = '\n';
2982 if (*src_text_ptr != ' ')
2983 *dst_text_ptr++ = *src_text_ptr;
2988 *dst_text_ptr = '\0';
2990 text_final = text_door_style;
2993 setRequestPosition(&sx, &sy, FALSE);
2995 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2997 for (y = 0; y < y_steps; y++)
2998 for (x = 0; x < x_steps; x++)
2999 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3000 x, y, x_steps, y_steps,
3001 tile_size, tile_size);
3003 // force DOOR font inside door area
3004 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3006 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3007 line_length, -1, max_lines, line_spacing, mask_mode,
3008 request.autowrap, request.centered, FALSE);
3012 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3013 RedrawGadget(tool_gadget[i]);
3015 // store readily prepared envelope request for later use when animating
3016 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3018 if (text_door_style)
3019 free(text_door_style);
3022 static void AnimateEnvelopeRequest(int anim_mode, int action)
3024 int graphic = IMG_BACKGROUND_REQUEST;
3025 boolean draw_masked = graphic_info[graphic].draw_masked;
3026 int delay_value_normal = request.step_delay;
3027 int delay_value_fast = delay_value_normal / 2;
3028 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3029 boolean no_delay = (tape.warp_forward);
3030 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3031 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3032 unsigned int anim_delay = 0;
3034 int tile_size = MAX(request.step_offset, 1);
3035 int max_xsize = request.width / tile_size;
3036 int max_ysize = request.height / tile_size;
3037 int max_xsize_inner = max_xsize - 2;
3038 int max_ysize_inner = max_ysize - 2;
3040 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3041 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3042 int xend = max_xsize_inner;
3043 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3044 int xstep = (xstart < xend ? 1 : 0);
3045 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3047 int end = MAX(xend - xstart, yend - ystart);
3050 if (setup.quick_doors)
3057 for (i = start; i <= end; i++)
3059 int last_frame = end; // last frame of this "for" loop
3060 int x = xstart + i * xstep;
3061 int y = ystart + i * ystep;
3062 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3063 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3064 int xsize_size_left = (xsize - 1) * tile_size;
3065 int ysize_size_top = (ysize - 1) * tile_size;
3066 int max_xsize_pos = (max_xsize - 1) * tile_size;
3067 int max_ysize_pos = (max_ysize - 1) * tile_size;
3068 int width = xsize * tile_size;
3069 int height = ysize * tile_size;
3074 setRequestPosition(&src_x, &src_y, FALSE);
3075 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3077 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3079 for (yy = 0; yy < 2; yy++)
3081 for (xx = 0; xx < 2; xx++)
3083 int src_xx = src_x + xx * max_xsize_pos;
3084 int src_yy = src_y + yy * max_ysize_pos;
3085 int dst_xx = dst_x + xx * xsize_size_left;
3086 int dst_yy = dst_y + yy * ysize_size_top;
3087 int xx_size = (xx ? tile_size : xsize_size_left);
3088 int yy_size = (yy ? tile_size : ysize_size_top);
3091 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3092 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3094 BlitBitmap(bitmap_db_store_2, backbuffer,
3095 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3099 redraw_mask |= REDRAW_FIELD;
3103 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3106 ClearAutoRepeatKeyEvents();
3109 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3111 int graphic = IMG_BACKGROUND_REQUEST;
3112 int sound_opening = SND_REQUEST_OPENING;
3113 int sound_closing = SND_REQUEST_CLOSING;
3114 int anim_mode_1 = request.anim_mode; // (higher priority)
3115 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3116 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3117 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3118 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3120 if (game_status == GAME_MODE_PLAYING)
3121 BlitScreenToBitmap(backbuffer);
3123 SetDrawtoField(DRAW_TO_BACKBUFFER);
3125 // SetDrawBackgroundMask(REDRAW_NONE);
3127 if (action == ACTION_OPENING)
3129 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3131 if (req_state & REQ_ASK)
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3134 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3136 else if (req_state & REQ_CONFIRM)
3138 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3140 else if (req_state & REQ_PLAYER)
3142 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3143 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3144 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3145 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3148 DrawEnvelopeRequest(text);
3151 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3153 if (action == ACTION_OPENING)
3155 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3157 if (anim_mode == ANIM_DEFAULT)
3158 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3160 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3164 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3166 if (anim_mode != ANIM_NONE)
3167 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3169 if (anim_mode == ANIM_DEFAULT)
3170 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3173 game.envelope_active = FALSE;
3175 if (action == ACTION_CLOSING)
3176 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3178 // SetDrawBackgroundMask(last_draw_background_mask);
3180 redraw_mask |= REDRAW_FIELD;
3184 if (action == ACTION_CLOSING &&
3185 game_status == GAME_MODE_PLAYING &&
3186 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3187 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3190 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3192 if (IS_MM_WALL(element))
3194 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3200 int graphic = el2preimg(element);
3202 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3203 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3208 void DrawLevel(int draw_background_mask)
3212 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3213 SetDrawBackgroundMask(draw_background_mask);
3217 for (x = BX1; x <= BX2; x++)
3218 for (y = BY1; y <= BY2; y++)
3219 DrawScreenField(x, y);
3221 redraw_mask |= REDRAW_FIELD;
3224 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3229 for (x = 0; x < size_x; x++)
3230 for (y = 0; y < size_y; y++)
3231 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3233 redraw_mask |= REDRAW_FIELD;
3236 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3240 for (x = 0; x < size_x; x++)
3241 for (y = 0; y < size_y; y++)
3242 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3244 redraw_mask |= REDRAW_FIELD;
3247 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3249 boolean show_level_border = (BorderElement != EL_EMPTY);
3250 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3251 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3252 int tile_size = preview.tile_size;
3253 int preview_width = preview.xsize * tile_size;
3254 int preview_height = preview.ysize * tile_size;
3255 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3256 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3257 int real_preview_width = real_preview_xsize * tile_size;
3258 int real_preview_height = real_preview_ysize * tile_size;
3259 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3260 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3263 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3266 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3268 dst_x += (preview_width - real_preview_width) / 2;
3269 dst_y += (preview_height - real_preview_height) / 2;
3271 for (x = 0; x < real_preview_xsize; x++)
3273 for (y = 0; y < real_preview_ysize; y++)
3275 int lx = from_x + x + (show_level_border ? -1 : 0);
3276 int ly = from_y + y + (show_level_border ? -1 : 0);
3277 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3278 getBorderElement(lx, ly));
3280 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3281 element, tile_size);
3285 redraw_mask |= REDRAW_FIELD;
3288 #define MICROLABEL_EMPTY 0
3289 #define MICROLABEL_LEVEL_NAME 1
3290 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3291 #define MICROLABEL_LEVEL_AUTHOR 3
3292 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3293 #define MICROLABEL_IMPORTED_FROM 5
3294 #define MICROLABEL_IMPORTED_BY_HEAD 6
3295 #define MICROLABEL_IMPORTED_BY 7
3297 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3299 int max_text_width = SXSIZE;
3300 int font_width = getFontWidth(font_nr);
3302 if (pos->align == ALIGN_CENTER)
3303 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3304 else if (pos->align == ALIGN_RIGHT)
3305 max_text_width = pos->x;
3307 max_text_width = SXSIZE - pos->x;
3309 return max_text_width / font_width;
3312 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3314 char label_text[MAX_OUTPUT_LINESIZE + 1];
3315 int max_len_label_text;
3316 int font_nr = pos->font;
3319 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3322 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3323 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3324 mode == MICROLABEL_IMPORTED_BY_HEAD)
3325 font_nr = pos->font_alt;
3327 max_len_label_text = getMaxTextLength(pos, font_nr);
3329 if (pos->size != -1)
3330 max_len_label_text = pos->size;
3332 for (i = 0; i < max_len_label_text; i++)
3333 label_text[i] = ' ';
3334 label_text[max_len_label_text] = '\0';
3336 if (strlen(label_text) > 0)
3337 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3340 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3341 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3342 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3343 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3344 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3345 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3346 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3347 max_len_label_text);
3348 label_text[max_len_label_text] = '\0';
3350 if (strlen(label_text) > 0)
3351 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3353 redraw_mask |= REDRAW_FIELD;
3356 static void DrawPreviewLevelLabel(int mode)
3358 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3361 static void DrawPreviewLevelInfo(int mode)
3363 if (mode == MICROLABEL_LEVEL_NAME)
3364 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3365 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3366 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3369 static void DrawPreviewLevelExt(boolean restart)
3371 static unsigned int scroll_delay = 0;
3372 static unsigned int label_delay = 0;
3373 static int from_x, from_y, scroll_direction;
3374 static int label_state, label_counter;
3375 unsigned int scroll_delay_value = preview.step_delay;
3376 boolean show_level_border = (BorderElement != EL_EMPTY);
3377 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3378 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3385 if (preview.anim_mode == ANIM_CENTERED)
3387 if (level_xsize > preview.xsize)
3388 from_x = (level_xsize - preview.xsize) / 2;
3389 if (level_ysize > preview.ysize)
3390 from_y = (level_ysize - preview.ysize) / 2;
3393 from_x += preview.xoffset;
3394 from_y += preview.yoffset;
3396 scroll_direction = MV_RIGHT;
3400 DrawPreviewLevelPlayfield(from_x, from_y);
3401 DrawPreviewLevelLabel(label_state);
3403 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3404 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3406 // initialize delay counters
3407 DelayReached(&scroll_delay, 0);
3408 DelayReached(&label_delay, 0);
3410 if (leveldir_current->name)
3412 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3413 char label_text[MAX_OUTPUT_LINESIZE + 1];
3414 int font_nr = pos->font;
3415 int max_len_label_text = getMaxTextLength(pos, font_nr);
3417 if (pos->size != -1)
3418 max_len_label_text = pos->size;
3420 strncpy(label_text, leveldir_current->name, max_len_label_text);
3421 label_text[max_len_label_text] = '\0';
3423 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3424 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3430 // scroll preview level, if needed
3431 if (preview.anim_mode != ANIM_NONE &&
3432 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3433 DelayReached(&scroll_delay, scroll_delay_value))
3435 switch (scroll_direction)
3440 from_x -= preview.step_offset;
3441 from_x = (from_x < 0 ? 0 : from_x);
3444 scroll_direction = MV_UP;
3448 if (from_x < level_xsize - preview.xsize)
3450 from_x += preview.step_offset;
3451 from_x = (from_x > level_xsize - preview.xsize ?
3452 level_xsize - preview.xsize : from_x);
3455 scroll_direction = MV_DOWN;
3461 from_y -= preview.step_offset;
3462 from_y = (from_y < 0 ? 0 : from_y);
3465 scroll_direction = MV_RIGHT;
3469 if (from_y < level_ysize - preview.ysize)
3471 from_y += preview.step_offset;
3472 from_y = (from_y > level_ysize - preview.ysize ?
3473 level_ysize - preview.ysize : from_y);
3476 scroll_direction = MV_LEFT;
3483 DrawPreviewLevelPlayfield(from_x, from_y);
3486 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3487 // redraw micro level label, if needed
3488 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3489 !strEqual(level.author, ANONYMOUS_NAME) &&
3490 !strEqual(level.author, leveldir_current->name) &&
3491 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3493 int max_label_counter = 23;
3495 if (leveldir_current->imported_from != NULL &&
3496 strlen(leveldir_current->imported_from) > 0)
3497 max_label_counter += 14;
3498 if (leveldir_current->imported_by != NULL &&
3499 strlen(leveldir_current->imported_by) > 0)
3500 max_label_counter += 14;
3502 label_counter = (label_counter + 1) % max_label_counter;
3503 label_state = (label_counter >= 0 && label_counter <= 7 ?
3504 MICROLABEL_LEVEL_NAME :
3505 label_counter >= 9 && label_counter <= 12 ?
3506 MICROLABEL_LEVEL_AUTHOR_HEAD :
3507 label_counter >= 14 && label_counter <= 21 ?
3508 MICROLABEL_LEVEL_AUTHOR :
3509 label_counter >= 23 && label_counter <= 26 ?
3510 MICROLABEL_IMPORTED_FROM_HEAD :
3511 label_counter >= 28 && label_counter <= 35 ?
3512 MICROLABEL_IMPORTED_FROM :
3513 label_counter >= 37 && label_counter <= 40 ?
3514 MICROLABEL_IMPORTED_BY_HEAD :
3515 label_counter >= 42 && label_counter <= 49 ?
3516 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3518 if (leveldir_current->imported_from == NULL &&
3519 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3520 label_state == MICROLABEL_IMPORTED_FROM))
3521 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3522 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3524 DrawPreviewLevelLabel(label_state);
3528 void DrawPreviewPlayers(void)
3530 if (game_status != GAME_MODE_MAIN)
3533 // do not draw preview players if level preview redefined, but players aren't
3534 if (preview.redefined && !menu.main.preview_players.redefined)
3537 boolean player_found[MAX_PLAYERS];
3538 int num_players = 0;
3541 for (i = 0; i < MAX_PLAYERS; i++)
3542 player_found[i] = FALSE;
3544 // check which players can be found in the level (simple approach)
3545 for (x = 0; x < lev_fieldx; x++)
3547 for (y = 0; y < lev_fieldy; y++)
3549 int element = level.field[x][y];
3551 if (ELEM_IS_PLAYER(element))
3553 int player_nr = GET_PLAYER_NR(element);
3555 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3557 if (!player_found[player_nr])
3560 player_found[player_nr] = TRUE;
3565 struct TextPosInfo *pos = &menu.main.preview_players;
3566 int tile_size = pos->tile_size;
3567 int border_size = pos->border_size;
3568 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3569 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3570 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3571 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3572 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3573 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3574 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3575 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3576 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3577 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3578 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3579 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3581 // clear area in which the players will be drawn
3582 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3583 max_players_width, max_players_height);
3585 if (!network.enabled && !setup.team_mode)
3588 // only draw players if level is suited for team mode
3589 if (num_players < 2)
3592 // draw all players that were found in the level
3593 for (i = 0; i < MAX_PLAYERS; i++)
3595 if (player_found[i])
3597 int graphic = el2img(EL_PLAYER_1 + i);
3599 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3601 xpos += player_xoffset;
3602 ypos += player_yoffset;
3607 void DrawPreviewLevelInitial(void)
3609 DrawPreviewLevelExt(TRUE);
3610 DrawPreviewPlayers();
3613 void DrawPreviewLevelAnimation(void)
3615 DrawPreviewLevelExt(FALSE);
3618 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3619 int border_size, int font_nr)
3621 int graphic = el2img(EL_PLAYER_1 + player_nr);
3622 int font_height = getFontHeight(font_nr);
3623 int player_height = MAX(tile_size, font_height);
3624 int xoffset_text = tile_size + border_size;
3625 int yoffset_text = (player_height - font_height) / 2;
3626 int yoffset_graphic = (player_height - tile_size) / 2;
3627 char *player_name = getNetworkPlayerName(player_nr + 1);
3629 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3631 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3634 static void DrawNetworkPlayersExt(boolean force)
3636 if (game_status != GAME_MODE_MAIN)
3639 if (!network.connected && !force)
3642 // do not draw network players if level preview redefined, but players aren't
3643 if (preview.redefined && !menu.main.network_players.redefined)
3646 int num_players = 0;
3649 for (i = 0; i < MAX_PLAYERS; i++)
3650 if (stored_player[i].connected_network)
3653 struct TextPosInfo *pos = &menu.main.network_players;
3654 int tile_size = pos->tile_size;
3655 int border_size = pos->border_size;
3656 int xoffset_text = tile_size + border_size;
3657 int font_nr = pos->font;
3658 int font_width = getFontWidth(font_nr);
3659 int font_height = getFontHeight(font_nr);
3660 int player_height = MAX(tile_size, font_height);
3661 int player_yoffset = player_height + border_size;
3662 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3663 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3664 int all_players_height = num_players * player_yoffset - border_size;
3665 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3666 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3667 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3669 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3670 max_players_width, max_players_height);
3672 // first draw local network player ...
3673 for (i = 0; i < MAX_PLAYERS; i++)
3675 if (stored_player[i].connected_network &&
3676 stored_player[i].connected_locally)
3678 char *player_name = getNetworkPlayerName(i + 1);
3679 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3680 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3682 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3684 ypos += player_yoffset;
3688 // ... then draw all other network players
3689 for (i = 0; i < MAX_PLAYERS; i++)
3691 if (stored_player[i].connected_network &&
3692 !stored_player[i].connected_locally)
3694 char *player_name = getNetworkPlayerName(i + 1);
3695 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3696 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3698 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3700 ypos += player_yoffset;
3705 void DrawNetworkPlayers(void)
3707 DrawNetworkPlayersExt(FALSE);
3710 void ClearNetworkPlayers(void)
3712 DrawNetworkPlayersExt(TRUE);
3715 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3716 int graphic, int sync_frame,
3719 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3721 if (mask_mode == USE_MASKING)
3722 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3724 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3727 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3728 int graphic, int sync_frame, int mask_mode)
3730 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3732 if (mask_mode == USE_MASKING)
3733 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3735 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3738 static void DrawGraphicAnimation(int x, int y, int graphic)
3740 int lx = LEVELX(x), ly = LEVELY(y);
3742 if (!IN_SCR_FIELD(x, y))
3745 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3746 graphic, GfxFrame[lx][ly], NO_MASKING);
3748 MarkTileDirty(x, y);
3751 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3753 int lx = LEVELX(x), ly = LEVELY(y);
3755 if (!IN_SCR_FIELD(x, y))
3758 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3759 graphic, GfxFrame[lx][ly], NO_MASKING);
3760 MarkTileDirty(x, y);
3763 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3765 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3768 void DrawLevelElementAnimation(int x, int y, int element)
3770 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3772 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3775 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3777 int sx = SCREENX(x), sy = SCREENY(y);
3779 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3782 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3785 DrawGraphicAnimation(sx, sy, graphic);
3788 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3789 DrawLevelFieldCrumbled(x, y);
3791 if (GFX_CRUMBLED(Feld[x][y]))
3792 DrawLevelFieldCrumbled(x, y);
3796 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3798 int sx = SCREENX(x), sy = SCREENY(y);
3801 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3804 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3806 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3809 DrawGraphicAnimation(sx, sy, graphic);
3811 if (GFX_CRUMBLED(element))
3812 DrawLevelFieldCrumbled(x, y);
3815 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3817 if (player->use_murphy)
3819 // this works only because currently only one player can be "murphy" ...
3820 static int last_horizontal_dir = MV_LEFT;
3821 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3823 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3824 last_horizontal_dir = move_dir;
3826 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3828 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3830 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3836 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3839 static boolean equalGraphics(int graphic1, int graphic2)
3841 struct GraphicInfo *g1 = &graphic_info[graphic1];
3842 struct GraphicInfo *g2 = &graphic_info[graphic2];
3844 return (g1->bitmap == g2->bitmap &&
3845 g1->src_x == g2->src_x &&
3846 g1->src_y == g2->src_y &&
3847 g1->anim_frames == g2->anim_frames &&
3848 g1->anim_delay == g2->anim_delay &&
3849 g1->anim_mode == g2->anim_mode);
3852 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3856 DRAW_PLAYER_STAGE_INIT = 0,
3857 DRAW_PLAYER_STAGE_LAST_FIELD,
3858 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3859 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3860 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3861 DRAW_PLAYER_STAGE_PLAYER,
3863 DRAW_PLAYER_STAGE_PLAYER,
3864 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3866 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3867 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3869 NUM_DRAW_PLAYER_STAGES
3872 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3874 static int static_last_player_graphic[MAX_PLAYERS];
3875 static int static_last_player_frame[MAX_PLAYERS];
3876 static boolean static_player_is_opaque[MAX_PLAYERS];
3877 static boolean draw_player[MAX_PLAYERS];
3878 int pnr = player->index_nr;
3880 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3882 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3883 static_last_player_frame[pnr] = player->Frame;
3884 static_player_is_opaque[pnr] = FALSE;
3886 draw_player[pnr] = TRUE;
3889 if (!draw_player[pnr])
3893 if (!IN_LEV_FIELD(player->jx, player->jy))
3895 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3896 printf("DrawPlayerField(): This should never happen!\n");
3898 draw_player[pnr] = FALSE;
3904 int last_player_graphic = static_last_player_graphic[pnr];
3905 int last_player_frame = static_last_player_frame[pnr];
3906 boolean player_is_opaque = static_player_is_opaque[pnr];
3908 int jx = player->jx;
3909 int jy = player->jy;
3910 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3911 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3912 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3913 int last_jx = (player->is_moving ? jx - dx : jx);
3914 int last_jy = (player->is_moving ? jy - dy : jy);
3915 int next_jx = jx + dx;
3916 int next_jy = jy + dy;
3917 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3918 int sx = SCREENX(jx);
3919 int sy = SCREENY(jy);
3920 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3921 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3922 int element = Feld[jx][jy];
3923 int last_element = Feld[last_jx][last_jy];
3924 int action = (player->is_pushing ? ACTION_PUSHING :
3925 player->is_digging ? ACTION_DIGGING :
3926 player->is_collecting ? ACTION_COLLECTING :
3927 player->is_moving ? ACTION_MOVING :
3928 player->is_snapping ? ACTION_SNAPPING :
3929 player->is_dropping ? ACTION_DROPPING :
3930 player->is_waiting ? player->action_waiting :
3933 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3935 // ------------------------------------------------------------------------
3936 // initialize drawing the player
3937 // ------------------------------------------------------------------------
3939 draw_player[pnr] = FALSE;
3941 // GfxElement[][] is set to the element the player is digging or collecting;
3942 // remove also for off-screen player if the player is not moving anymore
3943 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3944 GfxElement[jx][jy] = EL_UNDEFINED;
3946 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3949 if (element == EL_EXPLOSION)
3952 InitPlayerGfxAnimation(player, action, move_dir);
3954 draw_player[pnr] = TRUE;
3956 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3958 // ------------------------------------------------------------------------
3959 // draw things in the field the player is leaving, if needed
3960 // ------------------------------------------------------------------------
3962 if (!IN_SCR_FIELD(sx, sy))
3963 draw_player[pnr] = FALSE;
3965 if (!player->is_moving)
3968 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3970 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3972 if (last_element == EL_DYNAMITE_ACTIVE ||
3973 last_element == EL_EM_DYNAMITE_ACTIVE ||
3974 last_element == EL_SP_DISK_RED_ACTIVE)
3975 DrawDynamite(last_jx, last_jy);
3977 DrawLevelFieldThruMask(last_jx, last_jy);
3979 else 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 DrawLevelField(last_jx, last_jy);
3986 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3987 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3989 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3991 // ------------------------------------------------------------------------
3992 // draw things behind the player, if needed
3993 // ------------------------------------------------------------------------
3997 DrawLevelElement(jx, jy, Back[jx][jy]);
4002 if (IS_ACTIVE_BOMB(element))
4004 DrawLevelElement(jx, jy, EL_EMPTY);
4009 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4011 int old_element = GfxElement[jx][jy];
4012 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4013 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4015 if (GFX_CRUMBLED(old_element))
4016 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4018 DrawGraphic(sx, sy, old_graphic, frame);
4020 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4021 static_player_is_opaque[pnr] = TRUE;
4025 GfxElement[jx][jy] = EL_UNDEFINED;
4027 // make sure that pushed elements are drawn with correct frame rate
4028 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4030 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4031 GfxFrame[jx][jy] = player->StepFrame;
4033 DrawLevelField(jx, jy);
4036 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4038 // ------------------------------------------------------------------------
4039 // draw things the player is pushing, if needed
4040 // ------------------------------------------------------------------------
4042 if (!player->is_pushing || !player->is_moving)
4045 int gfx_frame = GfxFrame[jx][jy];
4047 if (!IS_MOVING(jx, jy)) // push movement already finished
4049 element = Feld[next_jx][next_jy];
4050 gfx_frame = GfxFrame[next_jx][next_jy];
4053 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4054 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4055 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4057 // draw background element under pushed element (like the Sokoban field)
4058 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4060 // this allows transparent pushing animation over non-black background
4063 DrawLevelElement(jx, jy, Back[jx][jy]);
4065 DrawLevelElement(jx, jy, EL_EMPTY);
4067 if (Back[next_jx][next_jy])
4068 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4070 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4072 else if (Back[next_jx][next_jy])
4073 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4075 int px = SCREENX(jx), py = SCREENY(jy);
4076 int pxx = (TILEX - ABS(sxx)) * dx;
4077 int pyy = (TILEY - ABS(syy)) * dy;
4080 // do not draw (EM style) pushing animation when pushing is finished
4081 // (two-tile animations usually do not contain start and end frame)
4082 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4083 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4085 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4087 // masked drawing is needed for EMC style (double) movement graphics
4088 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4089 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4092 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4094 // ------------------------------------------------------------------------
4095 // draw player himself
4096 // ------------------------------------------------------------------------
4098 int graphic = getPlayerGraphic(player, move_dir);
4100 // in the case of changed player action or direction, prevent the current
4101 // animation frame from being restarted for identical animations
4102 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4103 player->Frame = last_player_frame;
4105 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4107 if (player_is_opaque)
4108 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4110 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4112 if (SHIELD_ON(player))
4114 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4115 IMG_SHIELD_NORMAL_ACTIVE);
4116 frame = getGraphicAnimationFrame(graphic, -1);
4118 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4121 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4123 // ------------------------------------------------------------------------
4124 // draw things in front of player (active dynamite or dynabombs)
4125 // ------------------------------------------------------------------------
4127 if (IS_ACTIVE_BOMB(element))
4129 int graphic = el2img(element);
4130 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4132 if (game.emulation == EMU_SUPAPLEX)
4133 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4135 DrawGraphicThruMask(sx, sy, graphic, frame);
4138 if (player_is_moving && last_element == EL_EXPLOSION)
4140 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4141 GfxElement[last_jx][last_jy] : EL_EMPTY);
4142 int graphic = el_act2img(element, ACTION_EXPLODING);
4143 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4144 int phase = ExplodePhase[last_jx][last_jy] - 1;
4145 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4148 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4151 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4153 // ------------------------------------------------------------------------
4154 // draw elements the player is just walking/passing through/under
4155 // ------------------------------------------------------------------------
4157 if (player_is_moving)
4159 // handle the field the player is leaving ...
4160 if (IS_ACCESSIBLE_INSIDE(last_element))
4161 DrawLevelField(last_jx, last_jy);
4162 else if (IS_ACCESSIBLE_UNDER(last_element))
4163 DrawLevelFieldThruMask(last_jx, last_jy);
4166 // do not redraw accessible elements if the player is just pushing them
4167 if (!player_is_moving || !player->is_pushing)
4169 // ... and the field the player is entering
4170 if (IS_ACCESSIBLE_INSIDE(element))
4171 DrawLevelField(jx, jy);
4172 else if (IS_ACCESSIBLE_UNDER(element))
4173 DrawLevelFieldThruMask(jx, jy);
4176 MarkTileDirty(sx, sy);
4180 void DrawPlayer(struct PlayerInfo *player)
4184 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4185 DrawPlayerExt(player, i);
4188 void DrawAllPlayers(void)
4192 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4193 for (j = 0; j < MAX_PLAYERS; j++)
4194 if (stored_player[j].active)
4195 DrawPlayerExt(&stored_player[j], i);
4198 void DrawPlayerField(int x, int y)
4200 if (!IS_PLAYER(x, y))
4203 DrawPlayer(PLAYERINFO(x, y));
4206 // ----------------------------------------------------------------------------
4208 void WaitForEventToContinue(void)
4210 boolean still_wait = TRUE;
4212 if (program.headless)
4215 // simulate releasing mouse button over last gadget, if still pressed
4217 HandleGadgets(-1, -1, 0);
4219 button_status = MB_RELEASED;
4227 if (NextValidEvent(&event))
4231 case EVENT_BUTTONRELEASE:
4232 case EVENT_KEYPRESS:
4233 case SDL_CONTROLLERBUTTONDOWN:
4234 case SDL_JOYBUTTONDOWN:
4238 case EVENT_KEYRELEASE:
4239 ClearPlayerAction();
4243 HandleOtherEvents(&event);
4247 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4256 #define MAX_REQUEST_LINES 13
4257 #define MAX_REQUEST_LINE_FONT1_LEN 7
4258 #define MAX_REQUEST_LINE_FONT2_LEN 10
4260 static int RequestHandleEvents(unsigned int req_state)
4262 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4264 int width = request.width;
4265 int height = request.height;
4269 // when showing request dialog after game ended, deactivate game panel
4270 if (game_just_ended)
4271 game.panel.active = FALSE;
4273 game.request_active = TRUE;
4275 setRequestPosition(&sx, &sy, FALSE);
4277 button_status = MB_RELEASED;
4279 request_gadget_id = -1;
4284 if (game_just_ended)
4286 // the MM game engine does not use a special (scrollable) field buffer
4287 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4288 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4290 HandleGameActions();
4292 SetDrawtoField(DRAW_TO_BACKBUFFER);
4294 if (global.use_envelope_request)
4296 // copy current state of request area to middle of playfield area
4297 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4305 while (NextValidEvent(&event))
4309 case EVENT_BUTTONPRESS:
4310 case EVENT_BUTTONRELEASE:
4311 case EVENT_MOTIONNOTIFY:
4315 if (event.type == EVENT_MOTIONNOTIFY)
4320 motion_status = TRUE;
4321 mx = ((MotionEvent *) &event)->x;
4322 my = ((MotionEvent *) &event)->y;
4326 motion_status = FALSE;
4327 mx = ((ButtonEvent *) &event)->x;
4328 my = ((ButtonEvent *) &event)->y;
4329 if (event.type == EVENT_BUTTONPRESS)
4330 button_status = ((ButtonEvent *) &event)->button;
4332 button_status = MB_RELEASED;
4335 // this sets 'request_gadget_id'
4336 HandleGadgets(mx, my, button_status);
4338 switch (request_gadget_id)
4340 case TOOL_CTRL_ID_YES:
4343 case TOOL_CTRL_ID_NO:
4346 case TOOL_CTRL_ID_CONFIRM:
4347 result = TRUE | FALSE;
4350 case TOOL_CTRL_ID_PLAYER_1:
4353 case TOOL_CTRL_ID_PLAYER_2:
4356 case TOOL_CTRL_ID_PLAYER_3:
4359 case TOOL_CTRL_ID_PLAYER_4:
4370 case SDL_WINDOWEVENT:
4371 HandleWindowEvent((WindowEvent *) &event);
4374 case SDL_APP_WILLENTERBACKGROUND:
4375 case SDL_APP_DIDENTERBACKGROUND:
4376 case SDL_APP_WILLENTERFOREGROUND:
4377 case SDL_APP_DIDENTERFOREGROUND:
4378 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4381 case EVENT_KEYPRESS:
4383 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4388 if (req_state & REQ_CONFIRM)
4397 #if defined(KSYM_Rewind)
4398 case KSYM_Rewind: // for Amazon Fire TV remote
4407 #if defined(KSYM_FastForward)
4408 case KSYM_FastForward: // for Amazon Fire TV remote
4414 HandleKeysDebug(key, KEY_PRESSED);
4418 if (req_state & REQ_PLAYER)
4420 int old_player_nr = setup.network_player_nr;
4423 result = old_player_nr + 1;
4428 result = old_player_nr + 1;
4459 case EVENT_KEYRELEASE:
4460 ClearPlayerAction();
4463 case SDL_CONTROLLERBUTTONDOWN:
4464 switch (event.cbutton.button)
4466 case SDL_CONTROLLER_BUTTON_A:
4467 case SDL_CONTROLLER_BUTTON_X:
4468 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4469 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4473 case SDL_CONTROLLER_BUTTON_B:
4474 case SDL_CONTROLLER_BUTTON_Y:
4475 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4476 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4477 case SDL_CONTROLLER_BUTTON_BACK:
4482 if (req_state & REQ_PLAYER)
4484 int old_player_nr = setup.network_player_nr;
4487 result = old_player_nr + 1;
4489 switch (event.cbutton.button)
4491 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4492 case SDL_CONTROLLER_BUTTON_Y:
4496 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4497 case SDL_CONTROLLER_BUTTON_B:
4501 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4502 case SDL_CONTROLLER_BUTTON_A:
4506 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4507 case SDL_CONTROLLER_BUTTON_X:
4518 case SDL_CONTROLLERBUTTONUP:
4519 HandleJoystickEvent(&event);
4520 ClearPlayerAction();
4524 HandleOtherEvents(&event);
4529 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4531 int joy = AnyJoystick();
4533 if (joy & JOY_BUTTON_1)
4535 else if (joy & JOY_BUTTON_2)
4538 else if (AnyJoystick())
4540 int joy = AnyJoystick();
4542 if (req_state & REQ_PLAYER)
4546 else if (joy & JOY_RIGHT)
4548 else if (joy & JOY_DOWN)
4550 else if (joy & JOY_LEFT)
4555 if (game_just_ended)
4557 if (global.use_envelope_request)
4559 // copy back current state of pressed buttons inside request area
4560 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4567 game.request_active = FALSE;
4572 static boolean RequestDoor(char *text, unsigned int req_state)
4574 unsigned int old_door_state;
4575 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4576 int font_nr = FONT_TEXT_2;
4581 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4583 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4584 font_nr = FONT_TEXT_1;
4587 if (game_status == GAME_MODE_PLAYING)
4588 BlitScreenToBitmap(backbuffer);
4590 // disable deactivated drawing when quick-loading level tape recording
4591 if (tape.playing && tape.deactivate_display)
4592 TapeDeactivateDisplayOff(TRUE);
4594 SetMouseCursor(CURSOR_DEFAULT);
4596 // pause network game while waiting for request to answer
4597 if (network.enabled &&
4598 game_status == GAME_MODE_PLAYING &&
4599 !game.all_players_gone &&
4600 req_state & REQUEST_WAIT_FOR_INPUT)
4601 SendToServer_PausePlaying();
4603 old_door_state = GetDoorState();
4605 // simulate releasing mouse button over last gadget, if still pressed
4607 HandleGadgets(-1, -1, 0);
4611 // draw released gadget before proceeding
4614 if (old_door_state & DOOR_OPEN_1)
4616 CloseDoor(DOOR_CLOSE_1);
4618 // save old door content
4619 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4620 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4623 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4624 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4626 // clear door drawing field
4627 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4629 // force DOOR font inside door area
4630 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4632 // write text for request
4633 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4635 char text_line[max_request_line_len + 1];
4641 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4643 tc = *(text_ptr + tx);
4644 // if (!tc || tc == ' ')
4645 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4649 if ((tc == '?' || tc == '!') && tl == 0)
4659 strncpy(text_line, text_ptr, tl);
4662 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4663 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4664 text_line, font_nr);
4666 text_ptr += tl + (tc == ' ' ? 1 : 0);
4667 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4672 if (req_state & REQ_ASK)
4674 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4675 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4677 else if (req_state & REQ_CONFIRM)
4679 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4681 else if (req_state & REQ_PLAYER)
4683 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4684 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4685 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4686 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4689 // copy request gadgets to door backbuffer
4690 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4692 OpenDoor(DOOR_OPEN_1);
4694 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4696 if (game_status == GAME_MODE_PLAYING)
4698 SetPanelBackground();
4699 SetDrawBackgroundMask(REDRAW_DOOR_1);
4703 SetDrawBackgroundMask(REDRAW_FIELD);
4709 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4711 // ---------- handle request buttons ----------
4712 result = RequestHandleEvents(req_state);
4716 if (!(req_state & REQ_STAY_OPEN))
4718 CloseDoor(DOOR_CLOSE_1);
4720 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4721 (req_state & REQ_REOPEN))
4722 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4727 if (game_status == GAME_MODE_PLAYING)
4729 SetPanelBackground();
4730 SetDrawBackgroundMask(REDRAW_DOOR_1);
4734 SetDrawBackgroundMask(REDRAW_FIELD);
4737 // continue network game after request
4738 if (network.enabled &&
4739 game_status == GAME_MODE_PLAYING &&
4740 !game.all_players_gone &&
4741 req_state & REQUEST_WAIT_FOR_INPUT)
4742 SendToServer_ContinuePlaying();
4744 // restore deactivated drawing when quick-loading level tape recording
4745 if (tape.playing && tape.deactivate_display)
4746 TapeDeactivateDisplayOn();
4751 static boolean RequestEnvelope(char *text, unsigned int req_state)
4755 if (game_status == GAME_MODE_PLAYING)
4756 BlitScreenToBitmap(backbuffer);
4758 // disable deactivated drawing when quick-loading level tape recording
4759 if (tape.playing && tape.deactivate_display)
4760 TapeDeactivateDisplayOff(TRUE);
4762 SetMouseCursor(CURSOR_DEFAULT);
4764 // pause network game while waiting for request to answer
4765 if (network.enabled &&
4766 game_status == GAME_MODE_PLAYING &&
4767 !game.all_players_gone &&
4768 req_state & REQUEST_WAIT_FOR_INPUT)
4769 SendToServer_PausePlaying();
4771 // simulate releasing mouse button over last gadget, if still pressed
4773 HandleGadgets(-1, -1, 0);
4777 // (replace with setting corresponding request background)
4778 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4779 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4781 // clear door drawing field
4782 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4784 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4786 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4788 if (game_status == GAME_MODE_PLAYING)
4790 SetPanelBackground();
4791 SetDrawBackgroundMask(REDRAW_DOOR_1);
4795 SetDrawBackgroundMask(REDRAW_FIELD);
4801 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4803 // ---------- handle request buttons ----------
4804 result = RequestHandleEvents(req_state);
4808 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4812 if (game_status == GAME_MODE_PLAYING)
4814 SetPanelBackground();
4815 SetDrawBackgroundMask(REDRAW_DOOR_1);
4819 SetDrawBackgroundMask(REDRAW_FIELD);
4822 // continue network game after request
4823 if (network.enabled &&
4824 game_status == GAME_MODE_PLAYING &&
4825 !game.all_players_gone &&
4826 req_state & REQUEST_WAIT_FOR_INPUT)
4827 SendToServer_ContinuePlaying();
4829 // restore deactivated drawing when quick-loading level tape recording
4830 if (tape.playing && tape.deactivate_display)
4831 TapeDeactivateDisplayOn();
4836 boolean Request(char *text, unsigned int req_state)
4838 boolean overlay_enabled = GetOverlayEnabled();
4841 SetOverlayEnabled(FALSE);
4843 if (global.use_envelope_request)
4844 result = RequestEnvelope(text, req_state);
4846 result = RequestDoor(text, req_state);
4848 SetOverlayEnabled(overlay_enabled);
4853 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4855 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4856 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4859 if (dpo1->sort_priority != dpo2->sort_priority)
4860 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4862 compare_result = dpo1->nr - dpo2->nr;
4864 return compare_result;
4867 void InitGraphicCompatibilityInfo_Doors(void)
4873 struct DoorInfo *door;
4877 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4878 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4880 { -1, -1, -1, NULL }
4882 struct Rect door_rect_list[] =
4884 { DX, DY, DXSIZE, DYSIZE },
4885 { VX, VY, VXSIZE, VYSIZE }
4889 for (i = 0; doors[i].door_token != -1; i++)
4891 int door_token = doors[i].door_token;
4892 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4893 int part_1 = doors[i].part_1;
4894 int part_8 = doors[i].part_8;
4895 int part_2 = part_1 + 1;
4896 int part_3 = part_1 + 2;
4897 struct DoorInfo *door = doors[i].door;
4898 struct Rect *door_rect = &door_rect_list[door_index];
4899 boolean door_gfx_redefined = FALSE;
4901 // check if any door part graphic definitions have been redefined
4903 for (j = 0; door_part_controls[j].door_token != -1; j++)
4905 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4906 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4908 if (dpc->door_token == door_token && fi->redefined)
4909 door_gfx_redefined = TRUE;
4912 // check for old-style door graphic/animation modifications
4914 if (!door_gfx_redefined)
4916 if (door->anim_mode & ANIM_STATIC_PANEL)
4918 door->panel.step_xoffset = 0;
4919 door->panel.step_yoffset = 0;
4922 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4924 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4925 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4926 int num_door_steps, num_panel_steps;
4928 // remove door part graphics other than the two default wings
4930 for (j = 0; door_part_controls[j].door_token != -1; j++)
4932 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4933 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4935 if (dpc->graphic >= part_3 &&
4936 dpc->graphic <= part_8)
4940 // set graphics and screen positions of the default wings
4942 g_part_1->width = door_rect->width;
4943 g_part_1->height = door_rect->height;
4944 g_part_2->width = door_rect->width;
4945 g_part_2->height = door_rect->height;
4946 g_part_2->src_x = door_rect->width;
4947 g_part_2->src_y = g_part_1->src_y;
4949 door->part_2.x = door->part_1.x;
4950 door->part_2.y = door->part_1.y;
4952 if (door->width != -1)
4954 g_part_1->width = door->width;
4955 g_part_2->width = door->width;
4957 // special treatment for graphics and screen position of right wing
4958 g_part_2->src_x += door_rect->width - door->width;
4959 door->part_2.x += door_rect->width - door->width;
4962 if (door->height != -1)
4964 g_part_1->height = door->height;
4965 g_part_2->height = door->height;
4967 // special treatment for graphics and screen position of bottom wing
4968 g_part_2->src_y += door_rect->height - door->height;
4969 door->part_2.y += door_rect->height - door->height;
4972 // set animation delays for the default wings and panels
4974 door->part_1.step_delay = door->step_delay;
4975 door->part_2.step_delay = door->step_delay;
4976 door->panel.step_delay = door->step_delay;
4978 // set animation draw order for the default wings
4980 door->part_1.sort_priority = 2; // draw left wing over ...
4981 door->part_2.sort_priority = 1; // ... right wing
4983 // set animation draw offset for the default wings
4985 if (door->anim_mode & ANIM_HORIZONTAL)
4987 door->part_1.step_xoffset = door->step_offset;
4988 door->part_1.step_yoffset = 0;
4989 door->part_2.step_xoffset = door->step_offset * -1;
4990 door->part_2.step_yoffset = 0;
4992 num_door_steps = g_part_1->width / door->step_offset;
4994 else // ANIM_VERTICAL
4996 door->part_1.step_xoffset = 0;
4997 door->part_1.step_yoffset = door->step_offset;
4998 door->part_2.step_xoffset = 0;
4999 door->part_2.step_yoffset = door->step_offset * -1;
5001 num_door_steps = g_part_1->height / door->step_offset;
5004 // set animation draw offset for the default panels
5006 if (door->step_offset > 1)
5008 num_panel_steps = 2 * door_rect->height / door->step_offset;
5009 door->panel.start_step = num_panel_steps - num_door_steps;
5010 door->panel.start_step_closing = door->panel.start_step;
5014 num_panel_steps = door_rect->height / door->step_offset;
5015 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5016 door->panel.start_step_closing = door->panel.start_step;
5017 door->panel.step_delay *= 2;
5024 void InitDoors(void)
5028 for (i = 0; door_part_controls[i].door_token != -1; i++)
5030 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5031 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5033 // initialize "start_step_opening" and "start_step_closing", if needed
5034 if (dpc->pos->start_step_opening == 0 &&
5035 dpc->pos->start_step_closing == 0)
5037 // dpc->pos->start_step_opening = dpc->pos->start_step;
5038 dpc->pos->start_step_closing = dpc->pos->start_step;
5041 // fill structure for door part draw order (sorted below)
5043 dpo->sort_priority = dpc->pos->sort_priority;
5046 // sort door part controls according to sort_priority and graphic number
5047 qsort(door_part_order, MAX_DOOR_PARTS,
5048 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5051 unsigned int OpenDoor(unsigned int door_state)
5053 if (door_state & DOOR_COPY_BACK)
5055 if (door_state & DOOR_OPEN_1)
5056 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5057 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5059 if (door_state & DOOR_OPEN_2)
5060 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5061 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5063 door_state &= ~DOOR_COPY_BACK;
5066 return MoveDoor(door_state);
5069 unsigned int CloseDoor(unsigned int door_state)
5071 unsigned int old_door_state = GetDoorState();
5073 if (!(door_state & DOOR_NO_COPY_BACK))
5075 if (old_door_state & DOOR_OPEN_1)
5076 BlitBitmap(backbuffer, bitmap_db_door_1,
5077 DX, DY, DXSIZE, DYSIZE, 0, 0);
5079 if (old_door_state & DOOR_OPEN_2)
5080 BlitBitmap(backbuffer, bitmap_db_door_2,
5081 VX, VY, VXSIZE, VYSIZE, 0, 0);
5083 door_state &= ~DOOR_NO_COPY_BACK;
5086 return MoveDoor(door_state);
5089 unsigned int GetDoorState(void)
5091 return MoveDoor(DOOR_GET_STATE);
5094 unsigned int SetDoorState(unsigned int door_state)
5096 return MoveDoor(door_state | DOOR_SET_STATE);
5099 static int euclid(int a, int b)
5101 return (b ? euclid(b, a % b) : a);
5104 unsigned int MoveDoor(unsigned int door_state)
5106 struct Rect door_rect_list[] =
5108 { DX, DY, DXSIZE, DYSIZE },
5109 { VX, VY, VXSIZE, VYSIZE }
5111 static int door1 = DOOR_CLOSE_1;
5112 static int door2 = DOOR_CLOSE_2;
5113 unsigned int door_delay = 0;
5114 unsigned int door_delay_value;
5117 if (door_state == DOOR_GET_STATE)
5118 return (door1 | door2);
5120 if (door_state & DOOR_SET_STATE)
5122 if (door_state & DOOR_ACTION_1)
5123 door1 = door_state & DOOR_ACTION_1;
5124 if (door_state & DOOR_ACTION_2)
5125 door2 = door_state & DOOR_ACTION_2;
5127 return (door1 | door2);
5130 if (!(door_state & DOOR_FORCE_REDRAW))
5132 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5133 door_state &= ~DOOR_OPEN_1;
5134 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5135 door_state &= ~DOOR_CLOSE_1;
5136 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5137 door_state &= ~DOOR_OPEN_2;
5138 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5139 door_state &= ~DOOR_CLOSE_2;
5142 if (global.autoplay_leveldir)
5144 door_state |= DOOR_NO_DELAY;
5145 door_state &= ~DOOR_CLOSE_ALL;
5148 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5149 door_state |= DOOR_NO_DELAY;
5151 if (door_state & DOOR_ACTION)
5153 boolean door_panel_drawn[NUM_DOORS];
5154 boolean panel_has_doors[NUM_DOORS];
5155 boolean door_part_skip[MAX_DOOR_PARTS];
5156 boolean door_part_done[MAX_DOOR_PARTS];
5157 boolean door_part_done_all;
5158 int num_steps[MAX_DOOR_PARTS];
5159 int max_move_delay = 0; // delay for complete animations of all doors
5160 int max_step_delay = 0; // delay (ms) between two animation frames
5161 int num_move_steps = 0; // number of animation steps for all doors
5162 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5163 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5164 int current_move_delay = 0;
5168 for (i = 0; i < NUM_DOORS; i++)
5169 panel_has_doors[i] = FALSE;
5171 for (i = 0; i < MAX_DOOR_PARTS; i++)
5173 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5174 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5175 int door_token = dpc->door_token;
5177 door_part_done[i] = FALSE;
5178 door_part_skip[i] = (!(door_state & door_token) ||
5182 for (i = 0; i < MAX_DOOR_PARTS; i++)
5184 int nr = door_part_order[i].nr;
5185 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5186 struct DoorPartPosInfo *pos = dpc->pos;
5187 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5188 int door_token = dpc->door_token;
5189 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5190 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5191 int step_xoffset = ABS(pos->step_xoffset);
5192 int step_yoffset = ABS(pos->step_yoffset);
5193 int step_delay = pos->step_delay;
5194 int current_door_state = door_state & door_token;
5195 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5196 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5197 boolean part_opening = (is_panel ? door_closing : door_opening);
5198 int start_step = (part_opening ? pos->start_step_opening :
5199 pos->start_step_closing);
5200 float move_xsize = (step_xoffset ? g->width : 0);
5201 float move_ysize = (step_yoffset ? g->height : 0);
5202 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5203 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5204 int move_steps = (move_xsteps && move_ysteps ?
5205 MIN(move_xsteps, move_ysteps) :
5206 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5207 int move_delay = move_steps * step_delay;
5209 if (door_part_skip[nr])
5212 max_move_delay = MAX(max_move_delay, move_delay);
5213 max_step_delay = (max_step_delay == 0 ? step_delay :
5214 euclid(max_step_delay, step_delay));
5215 num_steps[nr] = move_steps;
5219 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5221 panel_has_doors[door_index] = TRUE;
5225 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5227 num_move_steps = max_move_delay / max_step_delay;
5228 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5230 door_delay_value = max_step_delay;
5232 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5234 start = num_move_steps - 1;
5238 // opening door sound has priority over simultaneously closing door
5239 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5241 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5243 if (door_state & DOOR_OPEN_1)
5244 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5245 if (door_state & DOOR_OPEN_2)
5246 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5248 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5250 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5252 if (door_state & DOOR_CLOSE_1)
5253 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5254 if (door_state & DOOR_CLOSE_2)
5255 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5259 for (k = start; k < num_move_steps; k++)
5261 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5263 door_part_done_all = TRUE;
5265 for (i = 0; i < NUM_DOORS; i++)
5266 door_panel_drawn[i] = FALSE;
5268 for (i = 0; i < MAX_DOOR_PARTS; i++)
5270 int nr = door_part_order[i].nr;
5271 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5272 struct DoorPartPosInfo *pos = dpc->pos;
5273 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5274 int door_token = dpc->door_token;
5275 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5276 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5277 boolean is_panel_and_door_has_closed = FALSE;
5278 struct Rect *door_rect = &door_rect_list[door_index];
5279 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5281 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5282 int current_door_state = door_state & door_token;
5283 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5284 boolean door_closing = !door_opening;
5285 boolean part_opening = (is_panel ? door_closing : door_opening);
5286 boolean part_closing = !part_opening;
5287 int start_step = (part_opening ? pos->start_step_opening :
5288 pos->start_step_closing);
5289 int step_delay = pos->step_delay;
5290 int step_factor = step_delay / max_step_delay;
5291 int k1 = (step_factor ? k / step_factor + 1 : k);
5292 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5293 int kk = MAX(0, k2);
5296 int src_x, src_y, src_xx, src_yy;
5297 int dst_x, dst_y, dst_xx, dst_yy;
5300 if (door_part_skip[nr])
5303 if (!(door_state & door_token))
5311 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5312 int kk_door = MAX(0, k2_door);
5313 int sync_frame = kk_door * door_delay_value;
5314 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5316 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5317 &g_src_x, &g_src_y);
5322 if (!door_panel_drawn[door_index])
5324 ClearRectangle(drawto, door_rect->x, door_rect->y,
5325 door_rect->width, door_rect->height);
5327 door_panel_drawn[door_index] = TRUE;
5330 // draw opening or closing door parts
5332 if (pos->step_xoffset < 0) // door part on right side
5335 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5338 if (dst_xx + width > door_rect->width)
5339 width = door_rect->width - dst_xx;
5341 else // door part on left side
5344 dst_xx = pos->x - kk * pos->step_xoffset;
5348 src_xx = ABS(dst_xx);
5352 width = g->width - src_xx;
5354 if (width > door_rect->width)
5355 width = door_rect->width;
5357 // printf("::: k == %d [%d] \n", k, start_step);
5360 if (pos->step_yoffset < 0) // door part on bottom side
5363 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5366 if (dst_yy + height > door_rect->height)
5367 height = door_rect->height - dst_yy;
5369 else // door part on top side
5372 dst_yy = pos->y - kk * pos->step_yoffset;
5376 src_yy = ABS(dst_yy);
5380 height = g->height - src_yy;
5383 src_x = g_src_x + src_xx;
5384 src_y = g_src_y + src_yy;
5386 dst_x = door_rect->x + dst_xx;
5387 dst_y = door_rect->y + dst_yy;
5389 is_panel_and_door_has_closed =
5392 panel_has_doors[door_index] &&
5393 k >= num_move_steps_doors_only - 1);
5395 if (width >= 0 && width <= g->width &&
5396 height >= 0 && height <= g->height &&
5397 !is_panel_and_door_has_closed)
5399 if (is_panel || !pos->draw_masked)
5400 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5403 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5407 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5409 if ((part_opening && (width < 0 || height < 0)) ||
5410 (part_closing && (width >= g->width && height >= g->height)))
5411 door_part_done[nr] = TRUE;
5413 // continue door part animations, but not panel after door has closed
5414 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5415 door_part_done_all = FALSE;
5418 if (!(door_state & DOOR_NO_DELAY))
5422 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5424 current_move_delay += max_step_delay;
5426 // prevent OS (Windows) from complaining about program not responding
5430 if (door_part_done_all)
5434 if (!(door_state & DOOR_NO_DELAY))
5436 // wait for specified door action post delay
5437 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5438 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5439 else if (door_state & DOOR_ACTION_1)
5440 door_delay_value = door_1.post_delay;
5441 else if (door_state & DOOR_ACTION_2)
5442 door_delay_value = door_2.post_delay;
5444 while (!DelayReached(&door_delay, door_delay_value))
5449 if (door_state & DOOR_ACTION_1)
5450 door1 = door_state & DOOR_ACTION_1;
5451 if (door_state & DOOR_ACTION_2)
5452 door2 = door_state & DOOR_ACTION_2;
5454 // draw masked border over door area
5455 DrawMaskedBorder(REDRAW_DOOR_1);
5456 DrawMaskedBorder(REDRAW_DOOR_2);
5458 ClearAutoRepeatKeyEvents();
5460 return (door1 | door2);
5463 static boolean useSpecialEditorDoor(void)
5465 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5466 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5468 // do not draw special editor door if editor border defined or redefined
5469 if (graphic_info[graphic].bitmap != NULL || redefined)
5472 // do not draw special editor door if global border defined to be empty
5473 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5476 // do not draw special editor door if viewport definitions do not match
5480 EY + EYSIZE != VY + VYSIZE)
5486 void DrawSpecialEditorDoor(void)
5488 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5489 int top_border_width = gfx1->width;
5490 int top_border_height = gfx1->height;
5491 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5492 int ex = EX - outer_border;
5493 int ey = EY - outer_border;
5494 int vy = VY - outer_border;
5495 int exsize = EXSIZE + 2 * outer_border;
5497 if (!useSpecialEditorDoor())
5500 // draw bigger level editor toolbox window
5501 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5502 top_border_width, top_border_height, ex, ey - top_border_height);
5503 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5504 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5506 redraw_mask |= REDRAW_ALL;
5509 void UndrawSpecialEditorDoor(void)
5511 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5512 int top_border_width = gfx1->width;
5513 int top_border_height = gfx1->height;
5514 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5515 int ex = EX - outer_border;
5516 int ey = EY - outer_border;
5517 int ey_top = ey - top_border_height;
5518 int exsize = EXSIZE + 2 * outer_border;
5519 int eysize = EYSIZE + 2 * outer_border;
5521 if (!useSpecialEditorDoor())
5524 // draw normal tape recorder window
5525 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5527 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5528 ex, ey_top, top_border_width, top_border_height,
5530 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5531 ex, ey, exsize, eysize, ex, ey);
5535 // if screen background is set to "[NONE]", clear editor toolbox window
5536 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5537 ClearRectangle(drawto, ex, ey, exsize, eysize);
5540 redraw_mask |= REDRAW_ALL;
5544 // ---------- new tool button stuff -------------------------------------------
5549 struct TextPosInfo *pos;
5552 } toolbutton_info[NUM_TOOL_BUTTONS] =
5555 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5556 TOOL_CTRL_ID_YES, "yes"
5559 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5560 TOOL_CTRL_ID_NO, "no"
5563 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5564 TOOL_CTRL_ID_CONFIRM, "confirm"
5567 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5568 TOOL_CTRL_ID_PLAYER_1, "player 1"
5571 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5572 TOOL_CTRL_ID_PLAYER_2, "player 2"
5575 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5576 TOOL_CTRL_ID_PLAYER_3, "player 3"
5579 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5580 TOOL_CTRL_ID_PLAYER_4, "player 4"
5584 void CreateToolButtons(void)
5588 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5590 int graphic = toolbutton_info[i].graphic;
5591 struct GraphicInfo *gfx = &graphic_info[graphic];
5592 struct TextPosInfo *pos = toolbutton_info[i].pos;
5593 struct GadgetInfo *gi;
5594 Bitmap *deco_bitmap = None;
5595 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5596 unsigned int event_mask = GD_EVENT_RELEASED;
5599 int gd_x = gfx->src_x;
5600 int gd_y = gfx->src_y;
5601 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5602 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5607 if (global.use_envelope_request)
5609 setRequestPosition(&dx, &dy, TRUE);
5611 // check if request buttons are outside of envelope and fix, if needed
5612 if (x < 0 || x + gfx->width > request.width ||
5613 y < 0 || y + gfx->height > request.height)
5615 if (id == TOOL_CTRL_ID_YES)
5618 y = request.height - 2 * request.border_size - gfx->height;
5620 else if (id == TOOL_CTRL_ID_NO)
5622 x = request.width - 2 * request.border_size - gfx->width;
5623 y = request.height - 2 * request.border_size - gfx->height;
5625 else if (id == TOOL_CTRL_ID_CONFIRM)
5627 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5628 y = request.height - 2 * request.border_size - gfx->height;
5630 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5632 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5634 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5635 y = request.height - 2 * request.border_size - gfx->height * 2;
5637 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5638 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5643 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5645 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5647 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5648 pos->size, &deco_bitmap, &deco_x, &deco_y);
5649 deco_xpos = (gfx->width - pos->size) / 2;
5650 deco_ypos = (gfx->height - pos->size) / 2;
5653 gi = CreateGadget(GDI_CUSTOM_ID, id,
5654 GDI_IMAGE_ID, graphic,
5655 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5658 GDI_WIDTH, gfx->width,
5659 GDI_HEIGHT, gfx->height,
5660 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5661 GDI_STATE, GD_BUTTON_UNPRESSED,
5662 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5663 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5664 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5665 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5666 GDI_DECORATION_SIZE, pos->size, pos->size,
5667 GDI_DECORATION_SHIFTING, 1, 1,
5668 GDI_DIRECT_DRAW, FALSE,
5669 GDI_EVENT_MASK, event_mask,
5670 GDI_CALLBACK_ACTION, HandleToolButtons,
5674 Error(ERR_EXIT, "cannot create gadget");
5676 tool_gadget[id] = gi;
5680 void FreeToolButtons(void)
5684 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5685 FreeGadget(tool_gadget[i]);
5688 static void UnmapToolButtons(void)
5692 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5693 UnmapGadget(tool_gadget[i]);
5696 static void HandleToolButtons(struct GadgetInfo *gi)
5698 request_gadget_id = gi->custom_id;
5701 static struct Mapping_EM_to_RND_object
5704 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5705 boolean is_backside; // backside of moving element
5711 em_object_mapping_list[] =
5714 Xblank, TRUE, FALSE,
5718 Yacid_splash_eB, FALSE, FALSE,
5719 EL_ACID_SPLASH_RIGHT, -1, -1
5722 Yacid_splash_wB, FALSE, FALSE,
5723 EL_ACID_SPLASH_LEFT, -1, -1
5726 #ifdef EM_ENGINE_BAD_ROLL
5728 Xstone_force_e, FALSE, FALSE,
5729 EL_ROCK, -1, MV_BIT_RIGHT
5732 Xstone_force_w, FALSE, FALSE,
5733 EL_ROCK, -1, MV_BIT_LEFT
5736 Xnut_force_e, FALSE, FALSE,
5737 EL_NUT, -1, MV_BIT_RIGHT
5740 Xnut_force_w, FALSE, FALSE,
5741 EL_NUT, -1, MV_BIT_LEFT
5744 Xspring_force_e, FALSE, FALSE,
5745 EL_SPRING, -1, MV_BIT_RIGHT
5748 Xspring_force_w, FALSE, FALSE,
5749 EL_SPRING, -1, MV_BIT_LEFT
5752 Xemerald_force_e, FALSE, FALSE,
5753 EL_EMERALD, -1, MV_BIT_RIGHT
5756 Xemerald_force_w, FALSE, FALSE,
5757 EL_EMERALD, -1, MV_BIT_LEFT
5760 Xdiamond_force_e, FALSE, FALSE,
5761 EL_DIAMOND, -1, MV_BIT_RIGHT
5764 Xdiamond_force_w, FALSE, FALSE,
5765 EL_DIAMOND, -1, MV_BIT_LEFT
5768 Xbomb_force_e, FALSE, FALSE,
5769 EL_BOMB, -1, MV_BIT_RIGHT
5772 Xbomb_force_w, FALSE, FALSE,
5773 EL_BOMB, -1, MV_BIT_LEFT
5775 #endif // EM_ENGINE_BAD_ROLL
5778 Xstone, TRUE, FALSE,
5782 Xstone_pause, FALSE, FALSE,
5786 Xstone_fall, FALSE, FALSE,
5790 Ystone_s, FALSE, FALSE,
5791 EL_ROCK, ACTION_FALLING, -1
5794 Ystone_sB, FALSE, TRUE,
5795 EL_ROCK, ACTION_FALLING, -1
5798 Ystone_e, FALSE, FALSE,
5799 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5802 Ystone_eB, FALSE, TRUE,
5803 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5806 Ystone_w, FALSE, FALSE,
5807 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5810 Ystone_wB, FALSE, TRUE,
5811 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5818 Xnut_pause, FALSE, FALSE,
5822 Xnut_fall, FALSE, FALSE,
5826 Ynut_s, FALSE, FALSE,
5827 EL_NUT, ACTION_FALLING, -1
5830 Ynut_sB, FALSE, TRUE,
5831 EL_NUT, ACTION_FALLING, -1
5834 Ynut_e, FALSE, FALSE,
5835 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5838 Ynut_eB, FALSE, TRUE,
5839 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5842 Ynut_w, FALSE, FALSE,
5843 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5846 Ynut_wB, FALSE, TRUE,
5847 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5850 Xbug_n, TRUE, FALSE,
5854 Xbug_e, TRUE, FALSE,
5855 EL_BUG_RIGHT, -1, -1
5858 Xbug_s, TRUE, FALSE,
5862 Xbug_w, TRUE, FALSE,
5866 Xbug_gon, FALSE, FALSE,
5870 Xbug_goe, FALSE, FALSE,
5871 EL_BUG_RIGHT, -1, -1
5874 Xbug_gos, FALSE, FALSE,
5878 Xbug_gow, FALSE, FALSE,
5882 Ybug_n, FALSE, FALSE,
5883 EL_BUG, ACTION_MOVING, MV_BIT_UP
5886 Ybug_nB, FALSE, TRUE,
5887 EL_BUG, ACTION_MOVING, MV_BIT_UP
5890 Ybug_e, FALSE, FALSE,
5891 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5894 Ybug_eB, FALSE, TRUE,
5895 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5898 Ybug_s, FALSE, FALSE,
5899 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5902 Ybug_sB, FALSE, TRUE,
5903 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5906 Ybug_w, FALSE, FALSE,
5907 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5910 Ybug_wB, FALSE, TRUE,
5911 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5914 Ybug_w_n, FALSE, FALSE,
5915 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5918 Ybug_n_e, FALSE, FALSE,
5919 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5922 Ybug_e_s, FALSE, FALSE,
5923 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5926 Ybug_s_w, FALSE, FALSE,
5927 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5930 Ybug_e_n, FALSE, FALSE,
5931 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5934 Ybug_s_e, FALSE, FALSE,
5935 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5938 Ybug_w_s, FALSE, FALSE,
5939 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5942 Ybug_n_w, FALSE, FALSE,
5943 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5946 Ybug_stone, FALSE, FALSE,
5947 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5950 Ybug_spring, FALSE, FALSE,
5951 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5954 Xtank_n, TRUE, FALSE,
5955 EL_SPACESHIP_UP, -1, -1
5958 Xtank_e, TRUE, FALSE,
5959 EL_SPACESHIP_RIGHT, -1, -1
5962 Xtank_s, TRUE, FALSE,
5963 EL_SPACESHIP_DOWN, -1, -1
5966 Xtank_w, TRUE, FALSE,
5967 EL_SPACESHIP_LEFT, -1, -1
5970 Xtank_gon, FALSE, FALSE,
5971 EL_SPACESHIP_UP, -1, -1
5974 Xtank_goe, FALSE, FALSE,
5975 EL_SPACESHIP_RIGHT, -1, -1
5978 Xtank_gos, FALSE, FALSE,
5979 EL_SPACESHIP_DOWN, -1, -1
5982 Xtank_gow, FALSE, FALSE,
5983 EL_SPACESHIP_LEFT, -1, -1
5986 Ytank_n, FALSE, FALSE,
5987 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5990 Ytank_nB, FALSE, TRUE,
5991 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5994 Ytank_e, FALSE, FALSE,
5995 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5998 Ytank_eB, FALSE, TRUE,
5999 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6002 Ytank_s, FALSE, FALSE,
6003 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6006 Ytank_sB, FALSE, TRUE,
6007 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6010 Ytank_w, FALSE, FALSE,
6011 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6014 Ytank_wB, FALSE, TRUE,
6015 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6018 Ytank_w_n, FALSE, FALSE,
6019 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6022 Ytank_n_e, FALSE, FALSE,
6023 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6026 Ytank_e_s, FALSE, FALSE,
6027 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6030 Ytank_s_w, FALSE, FALSE,
6031 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6034 Ytank_e_n, FALSE, FALSE,
6035 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6038 Ytank_s_e, FALSE, FALSE,
6039 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6042 Ytank_w_s, FALSE, FALSE,
6043 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6046 Ytank_n_w, FALSE, FALSE,
6047 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6050 Ytank_stone, FALSE, FALSE,
6051 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6054 Ytank_spring, FALSE, FALSE,
6055 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6058 Xandroid, TRUE, FALSE,
6059 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6062 Xandroid_1_n, FALSE, FALSE,
6063 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6066 Xandroid_2_n, FALSE, FALSE,
6067 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6070 Xandroid_1_e, FALSE, FALSE,
6071 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6074 Xandroid_2_e, FALSE, FALSE,
6075 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6078 Xandroid_1_w, FALSE, FALSE,
6079 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6082 Xandroid_2_w, FALSE, FALSE,
6083 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6086 Xandroid_1_s, FALSE, FALSE,
6087 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6090 Xandroid_2_s, FALSE, FALSE,
6091 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6094 Yandroid_n, FALSE, FALSE,
6095 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6098 Yandroid_nB, FALSE, TRUE,
6099 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6102 Yandroid_ne, FALSE, FALSE,
6103 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6106 Yandroid_neB, FALSE, TRUE,
6107 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6110 Yandroid_e, FALSE, FALSE,
6111 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6114 Yandroid_eB, FALSE, TRUE,
6115 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6118 Yandroid_se, FALSE, FALSE,
6119 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6122 Yandroid_seB, FALSE, TRUE,
6123 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6126 Yandroid_s, FALSE, FALSE,
6127 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6130 Yandroid_sB, FALSE, TRUE,
6131 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6134 Yandroid_sw, FALSE, FALSE,
6135 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6138 Yandroid_swB, FALSE, TRUE,
6139 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6142 Yandroid_w, FALSE, FALSE,
6143 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6146 Yandroid_wB, FALSE, TRUE,
6147 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6150 Yandroid_nw, FALSE, FALSE,
6151 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6154 Yandroid_nwB, FALSE, TRUE,
6155 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6158 Xspring, TRUE, FALSE,
6162 Xspring_pause, FALSE, FALSE,
6166 Xspring_e, FALSE, FALSE,
6170 Xspring_w, FALSE, FALSE,
6174 Xspring_fall, FALSE, FALSE,
6178 Yspring_s, FALSE, FALSE,
6179 EL_SPRING, ACTION_FALLING, -1
6182 Yspring_sB, FALSE, TRUE,
6183 EL_SPRING, ACTION_FALLING, -1
6186 Yspring_e, FALSE, FALSE,
6187 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6190 Yspring_eB, FALSE, TRUE,
6191 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6194 Yspring_w, FALSE, FALSE,
6195 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6198 Yspring_wB, FALSE, TRUE,
6199 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6202 Yspring_kill_e, FALSE, FALSE,
6203 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6206 Yspring_kill_eB, FALSE, TRUE,
6207 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6210 Yspring_kill_w, FALSE, FALSE,
6211 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6214 Yspring_kill_wB, FALSE, TRUE,
6215 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6218 Xeater_n, TRUE, FALSE,
6219 EL_YAMYAM_UP, -1, -1
6222 Xeater_e, TRUE, FALSE,
6223 EL_YAMYAM_RIGHT, -1, -1
6226 Xeater_w, TRUE, FALSE,
6227 EL_YAMYAM_LEFT, -1, -1
6230 Xeater_s, TRUE, FALSE,
6231 EL_YAMYAM_DOWN, -1, -1
6234 Yeater_n, FALSE, FALSE,
6235 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6238 Yeater_nB, FALSE, TRUE,
6239 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6242 Yeater_e, FALSE, FALSE,
6243 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6246 Yeater_eB, FALSE, TRUE,
6247 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6250 Yeater_s, FALSE, FALSE,
6251 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6254 Yeater_sB, FALSE, TRUE,
6255 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6258 Yeater_w, FALSE, FALSE,
6259 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6262 Yeater_wB, FALSE, TRUE,
6263 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6266 Yeater_stone, FALSE, FALSE,
6267 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6270 Yeater_spring, FALSE, FALSE,
6271 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6274 Xalien, TRUE, FALSE,
6278 Xalien_pause, FALSE, FALSE,
6282 Yalien_n, FALSE, FALSE,
6283 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6286 Yalien_nB, FALSE, TRUE,
6287 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6290 Yalien_e, FALSE, FALSE,
6291 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6294 Yalien_eB, FALSE, TRUE,
6295 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6298 Yalien_s, FALSE, FALSE,
6299 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6302 Yalien_sB, FALSE, TRUE,
6303 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6306 Yalien_w, FALSE, FALSE,
6307 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6310 Yalien_wB, FALSE, TRUE,
6311 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6314 Yalien_stone, FALSE, FALSE,
6315 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6318 Yalien_spring, FALSE, FALSE,
6319 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6322 Xemerald, TRUE, FALSE,
6326 Xemerald_pause, FALSE, FALSE,
6330 Xemerald_fall, FALSE, FALSE,
6334 Xemerald_shine, FALSE, FALSE,
6335 EL_EMERALD, ACTION_TWINKLING, -1
6338 Yemerald_s, FALSE, FALSE,
6339 EL_EMERALD, ACTION_FALLING, -1
6342 Yemerald_sB, FALSE, TRUE,
6343 EL_EMERALD, ACTION_FALLING, -1
6346 Yemerald_e, FALSE, FALSE,
6347 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6350 Yemerald_eB, FALSE, TRUE,
6351 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6354 Yemerald_w, FALSE, FALSE,
6355 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6358 Yemerald_wB, FALSE, TRUE,
6359 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6362 Yemerald_eat, FALSE, FALSE,
6363 EL_EMERALD, ACTION_COLLECTING, -1
6366 Yemerald_stone, FALSE, FALSE,
6367 EL_NUT, ACTION_BREAKING, -1
6370 Xdiamond, TRUE, FALSE,
6374 Xdiamond_pause, FALSE, FALSE,
6378 Xdiamond_fall, FALSE, FALSE,
6382 Xdiamond_shine, FALSE, FALSE,
6383 EL_DIAMOND, ACTION_TWINKLING, -1
6386 Ydiamond_s, FALSE, FALSE,
6387 EL_DIAMOND, ACTION_FALLING, -1
6390 Ydiamond_sB, FALSE, TRUE,
6391 EL_DIAMOND, ACTION_FALLING, -1
6394 Ydiamond_e, FALSE, FALSE,
6395 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6398 Ydiamond_eB, FALSE, TRUE,
6399 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6402 Ydiamond_w, FALSE, FALSE,
6403 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6406 Ydiamond_wB, FALSE, TRUE,
6407 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6410 Ydiamond_eat, FALSE, FALSE,
6411 EL_DIAMOND, ACTION_COLLECTING, -1
6414 Ydiamond_stone, FALSE, FALSE,
6415 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6418 Xdrip_fall, TRUE, FALSE,
6419 EL_AMOEBA_DROP, -1, -1
6422 Xdrip_stretch, FALSE, FALSE,
6423 EL_AMOEBA_DROP, ACTION_FALLING, -1
6426 Xdrip_stretchB, FALSE, TRUE,
6427 EL_AMOEBA_DROP, ACTION_FALLING, -1
6430 Xdrip_eat, FALSE, FALSE,
6431 EL_AMOEBA_DROP, ACTION_GROWING, -1
6434 Ydrip_s1, FALSE, FALSE,
6435 EL_AMOEBA_DROP, ACTION_FALLING, -1
6438 Ydrip_s1B, FALSE, TRUE,
6439 EL_AMOEBA_DROP, ACTION_FALLING, -1
6442 Ydrip_s2, FALSE, FALSE,
6443 EL_AMOEBA_DROP, ACTION_FALLING, -1
6446 Ydrip_s2B, FALSE, TRUE,
6447 EL_AMOEBA_DROP, ACTION_FALLING, -1
6454 Xbomb_pause, FALSE, FALSE,
6458 Xbomb_fall, FALSE, FALSE,
6462 Ybomb_s, FALSE, FALSE,
6463 EL_BOMB, ACTION_FALLING, -1
6466 Ybomb_sB, FALSE, TRUE,
6467 EL_BOMB, ACTION_FALLING, -1
6470 Ybomb_e, FALSE, FALSE,
6471 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6474 Ybomb_eB, FALSE, TRUE,
6475 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6478 Ybomb_w, FALSE, FALSE,
6479 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6482 Ybomb_wB, FALSE, TRUE,
6483 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6486 Ybomb_eat, FALSE, FALSE,
6487 EL_BOMB, ACTION_ACTIVATING, -1
6490 Xballoon, TRUE, FALSE,
6494 Yballoon_n, FALSE, FALSE,
6495 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6498 Yballoon_nB, FALSE, TRUE,
6499 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6502 Yballoon_e, FALSE, FALSE,
6503 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6506 Yballoon_eB, FALSE, TRUE,
6507 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6510 Yballoon_s, FALSE, FALSE,
6511 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6514 Yballoon_sB, FALSE, TRUE,
6515 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6518 Yballoon_w, FALSE, FALSE,
6519 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6522 Yballoon_wB, FALSE, TRUE,
6523 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6526 Xgrass, TRUE, FALSE,
6527 EL_EMC_GRASS, -1, -1
6530 Ygrass_nB, FALSE, FALSE,
6531 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6534 Ygrass_eB, FALSE, FALSE,
6535 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6538 Ygrass_sB, FALSE, FALSE,
6539 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6542 Ygrass_wB, FALSE, FALSE,
6543 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6550 Ydirt_nB, FALSE, FALSE,
6551 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6554 Ydirt_eB, FALSE, FALSE,
6555 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6558 Ydirt_sB, FALSE, FALSE,
6559 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6562 Ydirt_wB, FALSE, FALSE,
6563 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6566 Xacid_ne, TRUE, FALSE,
6567 EL_ACID_POOL_TOPRIGHT, -1, -1
6570 Xacid_se, TRUE, FALSE,
6571 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6574 Xacid_s, TRUE, FALSE,
6575 EL_ACID_POOL_BOTTOM, -1, -1
6578 Xacid_sw, TRUE, FALSE,
6579 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6582 Xacid_nw, TRUE, FALSE,
6583 EL_ACID_POOL_TOPLEFT, -1, -1
6586 Xacid_1, TRUE, FALSE,
6590 Xacid_2, FALSE, FALSE,
6594 Xacid_3, FALSE, FALSE,
6598 Xacid_4, FALSE, FALSE,
6602 Xacid_5, FALSE, FALSE,
6606 Xacid_6, FALSE, FALSE,
6610 Xacid_7, FALSE, FALSE,
6614 Xacid_8, FALSE, FALSE,
6618 Xball_1, TRUE, FALSE,
6619 EL_EMC_MAGIC_BALL, -1, -1
6622 Xball_1B, FALSE, FALSE,
6623 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6626 Xball_2, FALSE, FALSE,
6627 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6630 Xball_2B, FALSE, FALSE,
6631 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6634 Yball_eat, FALSE, FALSE,
6635 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6638 Ykey_1_eat, FALSE, FALSE,
6639 EL_EM_KEY_1, ACTION_COLLECTING, -1
6642 Ykey_2_eat, FALSE, FALSE,
6643 EL_EM_KEY_2, ACTION_COLLECTING, -1
6646 Ykey_3_eat, FALSE, FALSE,
6647 EL_EM_KEY_3, ACTION_COLLECTING, -1
6650 Ykey_4_eat, FALSE, FALSE,
6651 EL_EM_KEY_4, ACTION_COLLECTING, -1
6654 Ykey_5_eat, FALSE, FALSE,
6655 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6658 Ykey_6_eat, FALSE, FALSE,
6659 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6662 Ykey_7_eat, FALSE, FALSE,
6663 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6666 Ykey_8_eat, FALSE, FALSE,
6667 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6670 Ylenses_eat, FALSE, FALSE,
6671 EL_EMC_LENSES, ACTION_COLLECTING, -1
6674 Ymagnify_eat, FALSE, FALSE,
6675 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6678 Ygrass_eat, FALSE, FALSE,
6679 EL_EMC_GRASS, ACTION_SNAPPING, -1
6682 Ydirt_eat, FALSE, FALSE,
6683 EL_SAND, ACTION_SNAPPING, -1
6686 Xgrow_ns, TRUE, FALSE,
6687 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6690 Ygrow_ns_eat, FALSE, FALSE,
6691 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6694 Xgrow_ew, TRUE, FALSE,
6695 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6698 Ygrow_ew_eat, FALSE, FALSE,
6699 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6702 Xwonderwall, TRUE, FALSE,
6703 EL_MAGIC_WALL, -1, -1
6706 XwonderwallB, FALSE, FALSE,
6707 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6710 Xamoeba_1, TRUE, FALSE,
6711 EL_AMOEBA_DRY, ACTION_OTHER, -1
6714 Xamoeba_2, FALSE, FALSE,
6715 EL_AMOEBA_DRY, ACTION_OTHER, -1
6718 Xamoeba_3, FALSE, FALSE,
6719 EL_AMOEBA_DRY, ACTION_OTHER, -1
6722 Xamoeba_4, FALSE, FALSE,
6723 EL_AMOEBA_DRY, ACTION_OTHER, -1
6726 Xamoeba_5, TRUE, FALSE,
6727 EL_AMOEBA_WET, ACTION_OTHER, -1
6730 Xamoeba_6, FALSE, FALSE,
6731 EL_AMOEBA_WET, ACTION_OTHER, -1
6734 Xamoeba_7, FALSE, FALSE,
6735 EL_AMOEBA_WET, ACTION_OTHER, -1
6738 Xamoeba_8, FALSE, FALSE,
6739 EL_AMOEBA_WET, ACTION_OTHER, -1
6742 Xdoor_1, TRUE, FALSE,
6743 EL_EM_GATE_1, -1, -1
6746 Xdoor_2, TRUE, FALSE,
6747 EL_EM_GATE_2, -1, -1
6750 Xdoor_3, TRUE, FALSE,
6751 EL_EM_GATE_3, -1, -1
6754 Xdoor_4, TRUE, FALSE,
6755 EL_EM_GATE_4, -1, -1
6758 Xdoor_5, TRUE, FALSE,
6759 EL_EMC_GATE_5, -1, -1
6762 Xdoor_6, TRUE, FALSE,
6763 EL_EMC_GATE_6, -1, -1
6766 Xdoor_7, TRUE, FALSE,
6767 EL_EMC_GATE_7, -1, -1
6770 Xdoor_8, TRUE, FALSE,
6771 EL_EMC_GATE_8, -1, -1
6774 Xkey_1, TRUE, FALSE,
6778 Xkey_2, TRUE, FALSE,
6782 Xkey_3, TRUE, FALSE,
6786 Xkey_4, TRUE, FALSE,
6790 Xkey_5, TRUE, FALSE,
6791 EL_EMC_KEY_5, -1, -1
6794 Xkey_6, TRUE, FALSE,
6795 EL_EMC_KEY_6, -1, -1
6798 Xkey_7, TRUE, FALSE,
6799 EL_EMC_KEY_7, -1, -1
6802 Xkey_8, TRUE, FALSE,
6803 EL_EMC_KEY_8, -1, -1
6806 Xwind_n, TRUE, FALSE,
6807 EL_BALLOON_SWITCH_UP, -1, -1
6810 Xwind_e, TRUE, FALSE,
6811 EL_BALLOON_SWITCH_RIGHT, -1, -1
6814 Xwind_s, TRUE, FALSE,
6815 EL_BALLOON_SWITCH_DOWN, -1, -1
6818 Xwind_w, TRUE, FALSE,
6819 EL_BALLOON_SWITCH_LEFT, -1, -1
6822 Xwind_nesw, TRUE, FALSE,
6823 EL_BALLOON_SWITCH_ANY, -1, -1
6826 Xwind_stop, TRUE, FALSE,
6827 EL_BALLOON_SWITCH_NONE, -1, -1
6831 EL_EM_EXIT_CLOSED, -1, -1
6834 Xexit_1, TRUE, FALSE,
6835 EL_EM_EXIT_OPEN, -1, -1
6838 Xexit_2, FALSE, FALSE,
6839 EL_EM_EXIT_OPEN, -1, -1
6842 Xexit_3, FALSE, FALSE,
6843 EL_EM_EXIT_OPEN, -1, -1
6846 Xdynamite, TRUE, FALSE,
6847 EL_EM_DYNAMITE, -1, -1
6850 Ydynamite_eat, FALSE, FALSE,
6851 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6854 Xdynamite_1, TRUE, FALSE,
6855 EL_EM_DYNAMITE_ACTIVE, -1, -1
6858 Xdynamite_2, FALSE, FALSE,
6859 EL_EM_DYNAMITE_ACTIVE, -1, -1
6862 Xdynamite_3, FALSE, FALSE,
6863 EL_EM_DYNAMITE_ACTIVE, -1, -1
6866 Xdynamite_4, FALSE, FALSE,
6867 EL_EM_DYNAMITE_ACTIVE, -1, -1
6870 Xbumper, TRUE, FALSE,
6871 EL_EMC_SPRING_BUMPER, -1, -1
6874 XbumperB, FALSE, FALSE,
6875 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6878 Xwheel, TRUE, FALSE,
6879 EL_ROBOT_WHEEL, -1, -1
6882 XwheelB, FALSE, FALSE,
6883 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6886 Xswitch, TRUE, FALSE,
6887 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6890 XswitchB, FALSE, FALSE,
6891 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6895 EL_QUICKSAND_EMPTY, -1, -1
6898 Xsand_stone, TRUE, FALSE,
6899 EL_QUICKSAND_FULL, -1, -1
6902 Xsand_stonein_1, FALSE, TRUE,
6903 EL_ROCK, ACTION_FILLING, -1
6906 Xsand_stonein_2, FALSE, TRUE,
6907 EL_ROCK, ACTION_FILLING, -1
6910 Xsand_stonein_3, FALSE, TRUE,
6911 EL_ROCK, ACTION_FILLING, -1
6914 Xsand_stonein_4, FALSE, TRUE,
6915 EL_ROCK, ACTION_FILLING, -1
6918 Xsand_stonesand_1, FALSE, FALSE,
6919 EL_QUICKSAND_EMPTYING, -1, -1
6922 Xsand_stonesand_2, FALSE, FALSE,
6923 EL_QUICKSAND_EMPTYING, -1, -1
6926 Xsand_stonesand_3, FALSE, FALSE,
6927 EL_QUICKSAND_EMPTYING, -1, -1
6930 Xsand_stonesand_4, FALSE, FALSE,
6931 EL_QUICKSAND_EMPTYING, -1, -1
6934 Xsand_stonesand_quickout_1, FALSE, FALSE,
6935 EL_QUICKSAND_EMPTYING, -1, -1
6938 Xsand_stonesand_quickout_2, FALSE, FALSE,
6939 EL_QUICKSAND_EMPTYING, -1, -1
6942 Xsand_stoneout_1, FALSE, FALSE,
6943 EL_ROCK, ACTION_EMPTYING, -1
6946 Xsand_stoneout_2, FALSE, FALSE,
6947 EL_ROCK, ACTION_EMPTYING, -1
6950 Xsand_sandstone_1, FALSE, FALSE,
6951 EL_QUICKSAND_FILLING, -1, -1
6954 Xsand_sandstone_2, FALSE, FALSE,
6955 EL_QUICKSAND_FILLING, -1, -1
6958 Xsand_sandstone_3, FALSE, FALSE,
6959 EL_QUICKSAND_FILLING, -1, -1
6962 Xsand_sandstone_4, FALSE, FALSE,
6963 EL_QUICKSAND_FILLING, -1, -1
6966 Xplant, TRUE, FALSE,
6967 EL_EMC_PLANT, -1, -1
6970 Yplant, FALSE, FALSE,
6971 EL_EMC_PLANT, -1, -1
6974 Xlenses, TRUE, FALSE,
6975 EL_EMC_LENSES, -1, -1
6978 Xmagnify, TRUE, FALSE,
6979 EL_EMC_MAGNIFIER, -1, -1
6982 Xdripper, TRUE, FALSE,
6983 EL_EMC_DRIPPER, -1, -1
6986 XdripperB, FALSE, FALSE,
6987 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6990 Xfake_blank, TRUE, FALSE,
6991 EL_INVISIBLE_WALL, -1, -1
6994 Xfake_blankB, FALSE, FALSE,
6995 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6998 Xfake_grass, TRUE, FALSE,
6999 EL_EMC_FAKE_GRASS, -1, -1
7002 Xfake_grassB, FALSE, FALSE,
7003 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7006 Xfake_door_1, TRUE, FALSE,
7007 EL_EM_GATE_1_GRAY, -1, -1
7010 Xfake_door_2, TRUE, FALSE,
7011 EL_EM_GATE_2_GRAY, -1, -1
7014 Xfake_door_3, TRUE, FALSE,
7015 EL_EM_GATE_3_GRAY, -1, -1
7018 Xfake_door_4, TRUE, FALSE,
7019 EL_EM_GATE_4_GRAY, -1, -1
7022 Xfake_door_5, TRUE, FALSE,
7023 EL_EMC_GATE_5_GRAY, -1, -1
7026 Xfake_door_6, TRUE, FALSE,
7027 EL_EMC_GATE_6_GRAY, -1, -1
7030 Xfake_door_7, TRUE, FALSE,
7031 EL_EMC_GATE_7_GRAY, -1, -1
7034 Xfake_door_8, TRUE, FALSE,
7035 EL_EMC_GATE_8_GRAY, -1, -1
7038 Xfake_acid_1, TRUE, FALSE,
7039 EL_EMC_FAKE_ACID, -1, -1
7042 Xfake_acid_2, FALSE, FALSE,
7043 EL_EMC_FAKE_ACID, -1, -1
7046 Xfake_acid_3, FALSE, FALSE,
7047 EL_EMC_FAKE_ACID, -1, -1
7050 Xfake_acid_4, FALSE, FALSE,
7051 EL_EMC_FAKE_ACID, -1, -1
7054 Xfake_acid_5, FALSE, FALSE,
7055 EL_EMC_FAKE_ACID, -1, -1
7058 Xfake_acid_6, FALSE, FALSE,
7059 EL_EMC_FAKE_ACID, -1, -1
7062 Xfake_acid_7, FALSE, FALSE,
7063 EL_EMC_FAKE_ACID, -1, -1
7066 Xfake_acid_8, FALSE, FALSE,
7067 EL_EMC_FAKE_ACID, -1, -1
7070 Xsteel_1, TRUE, FALSE,
7071 EL_STEELWALL, -1, -1
7074 Xsteel_2, TRUE, FALSE,
7075 EL_EMC_STEELWALL_2, -1, -1
7078 Xsteel_3, TRUE, FALSE,
7079 EL_EMC_STEELWALL_3, -1, -1
7082 Xsteel_4, TRUE, FALSE,
7083 EL_EMC_STEELWALL_4, -1, -1
7086 Xwall_1, TRUE, FALSE,
7090 Xwall_2, TRUE, FALSE,
7091 EL_EMC_WALL_14, -1, -1
7094 Xwall_3, TRUE, FALSE,
7095 EL_EMC_WALL_15, -1, -1
7098 Xwall_4, TRUE, FALSE,
7099 EL_EMC_WALL_16, -1, -1
7102 Xround_wall_1, TRUE, FALSE,
7103 EL_WALL_SLIPPERY, -1, -1
7106 Xround_wall_2, TRUE, FALSE,
7107 EL_EMC_WALL_SLIPPERY_2, -1, -1
7110 Xround_wall_3, TRUE, FALSE,
7111 EL_EMC_WALL_SLIPPERY_3, -1, -1
7114 Xround_wall_4, TRUE, FALSE,
7115 EL_EMC_WALL_SLIPPERY_4, -1, -1
7118 Xdecor_1, TRUE, FALSE,
7119 EL_EMC_WALL_8, -1, -1
7122 Xdecor_2, TRUE, FALSE,
7123 EL_EMC_WALL_6, -1, -1
7126 Xdecor_3, TRUE, FALSE,
7127 EL_EMC_WALL_4, -1, -1
7130 Xdecor_4, TRUE, FALSE,
7131 EL_EMC_WALL_7, -1, -1
7134 Xdecor_5, TRUE, FALSE,
7135 EL_EMC_WALL_5, -1, -1
7138 Xdecor_6, TRUE, FALSE,
7139 EL_EMC_WALL_9, -1, -1
7142 Xdecor_7, TRUE, FALSE,
7143 EL_EMC_WALL_10, -1, -1
7146 Xdecor_8, TRUE, FALSE,
7147 EL_EMC_WALL_1, -1, -1
7150 Xdecor_9, TRUE, FALSE,
7151 EL_EMC_WALL_2, -1, -1
7154 Xdecor_10, TRUE, FALSE,
7155 EL_EMC_WALL_3, -1, -1
7158 Xdecor_11, TRUE, FALSE,
7159 EL_EMC_WALL_11, -1, -1
7162 Xdecor_12, TRUE, FALSE,
7163 EL_EMC_WALL_12, -1, -1
7166 Xalpha_0, TRUE, FALSE,
7167 EL_CHAR('0'), -1, -1
7170 Xalpha_1, TRUE, FALSE,
7171 EL_CHAR('1'), -1, -1
7174 Xalpha_2, TRUE, FALSE,
7175 EL_CHAR('2'), -1, -1
7178 Xalpha_3, TRUE, FALSE,
7179 EL_CHAR('3'), -1, -1
7182 Xalpha_4, TRUE, FALSE,
7183 EL_CHAR('4'), -1, -1
7186 Xalpha_5, TRUE, FALSE,
7187 EL_CHAR('5'), -1, -1
7190 Xalpha_6, TRUE, FALSE,
7191 EL_CHAR('6'), -1, -1
7194 Xalpha_7, TRUE, FALSE,
7195 EL_CHAR('7'), -1, -1
7198 Xalpha_8, TRUE, FALSE,
7199 EL_CHAR('8'), -1, -1
7202 Xalpha_9, TRUE, FALSE,
7203 EL_CHAR('9'), -1, -1
7206 Xalpha_excla, TRUE, FALSE,
7207 EL_CHAR('!'), -1, -1
7210 Xalpha_quote, TRUE, FALSE,
7211 EL_CHAR('"'), -1, -1
7214 Xalpha_comma, TRUE, FALSE,
7215 EL_CHAR(','), -1, -1
7218 Xalpha_minus, TRUE, FALSE,
7219 EL_CHAR('-'), -1, -1
7222 Xalpha_perio, TRUE, FALSE,
7223 EL_CHAR('.'), -1, -1
7226 Xalpha_colon, TRUE, FALSE,
7227 EL_CHAR(':'), -1, -1
7230 Xalpha_quest, TRUE, FALSE,
7231 EL_CHAR('?'), -1, -1
7234 Xalpha_a, TRUE, FALSE,
7235 EL_CHAR('A'), -1, -1
7238 Xalpha_b, TRUE, FALSE,
7239 EL_CHAR('B'), -1, -1
7242 Xalpha_c, TRUE, FALSE,
7243 EL_CHAR('C'), -1, -1
7246 Xalpha_d, TRUE, FALSE,
7247 EL_CHAR('D'), -1, -1
7250 Xalpha_e, TRUE, FALSE,
7251 EL_CHAR('E'), -1, -1
7254 Xalpha_f, TRUE, FALSE,
7255 EL_CHAR('F'), -1, -1
7258 Xalpha_g, TRUE, FALSE,
7259 EL_CHAR('G'), -1, -1
7262 Xalpha_h, TRUE, FALSE,
7263 EL_CHAR('H'), -1, -1
7266 Xalpha_i, TRUE, FALSE,
7267 EL_CHAR('I'), -1, -1
7270 Xalpha_j, TRUE, FALSE,
7271 EL_CHAR('J'), -1, -1
7274 Xalpha_k, TRUE, FALSE,
7275 EL_CHAR('K'), -1, -1
7278 Xalpha_l, TRUE, FALSE,
7279 EL_CHAR('L'), -1, -1
7282 Xalpha_m, TRUE, FALSE,
7283 EL_CHAR('M'), -1, -1
7286 Xalpha_n, TRUE, FALSE,
7287 EL_CHAR('N'), -1, -1
7290 Xalpha_o, TRUE, FALSE,
7291 EL_CHAR('O'), -1, -1
7294 Xalpha_p, TRUE, FALSE,
7295 EL_CHAR('P'), -1, -1
7298 Xalpha_q, TRUE, FALSE,
7299 EL_CHAR('Q'), -1, -1
7302 Xalpha_r, TRUE, FALSE,
7303 EL_CHAR('R'), -1, -1
7306 Xalpha_s, TRUE, FALSE,
7307 EL_CHAR('S'), -1, -1
7310 Xalpha_t, TRUE, FALSE,
7311 EL_CHAR('T'), -1, -1
7314 Xalpha_u, TRUE, FALSE,
7315 EL_CHAR('U'), -1, -1
7318 Xalpha_v, TRUE, FALSE,
7319 EL_CHAR('V'), -1, -1
7322 Xalpha_w, TRUE, FALSE,
7323 EL_CHAR('W'), -1, -1
7326 Xalpha_x, TRUE, FALSE,
7327 EL_CHAR('X'), -1, -1
7330 Xalpha_y, TRUE, FALSE,
7331 EL_CHAR('Y'), -1, -1
7334 Xalpha_z, TRUE, FALSE,
7335 EL_CHAR('Z'), -1, -1
7338 Xalpha_arrow_e, TRUE, FALSE,
7339 EL_CHAR('>'), -1, -1
7342 Xalpha_arrow_w, TRUE, FALSE,
7343 EL_CHAR('<'), -1, -1
7346 Xalpha_copyr, TRUE, FALSE,
7347 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7351 Xboom_bug, FALSE, FALSE,
7352 EL_BUG, ACTION_EXPLODING, -1
7355 Xboom_bomb, FALSE, FALSE,
7356 EL_BOMB, ACTION_EXPLODING, -1
7359 Xboom_android, FALSE, FALSE,
7360 EL_EMC_ANDROID, ACTION_OTHER, -1
7363 Xboom_1, FALSE, FALSE,
7364 EL_DEFAULT, ACTION_EXPLODING, -1
7367 Xboom_2, FALSE, FALSE,
7368 EL_DEFAULT, ACTION_EXPLODING, -1
7371 Znormal, FALSE, FALSE,
7375 Zdynamite, FALSE, FALSE,
7379 Zplayer, FALSE, FALSE,
7383 ZBORDER, FALSE, FALSE,
7393 static struct Mapping_EM_to_RND_player
7402 em_player_mapping_list[] =
7406 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7410 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7414 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7418 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7422 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7426 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7430 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7434 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7438 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7442 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7446 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7450 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7454 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7458 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7462 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7466 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7470 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7474 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7478 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7482 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7486 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7490 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7494 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7498 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7502 EL_PLAYER_1, ACTION_DEFAULT, -1,
7506 EL_PLAYER_2, ACTION_DEFAULT, -1,
7510 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7514 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7518 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7522 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7526 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7530 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7534 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7538 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7542 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7546 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7550 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7554 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7558 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7562 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7566 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7570 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7574 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7578 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7582 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7586 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7590 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7594 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7598 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7602 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7606 EL_PLAYER_3, ACTION_DEFAULT, -1,
7610 EL_PLAYER_4, ACTION_DEFAULT, -1,
7619 int map_element_RND_to_EM(int element_rnd)
7621 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7622 static boolean mapping_initialized = FALSE;
7624 if (!mapping_initialized)
7628 // return "Xalpha_quest" for all undefined elements in mapping array
7629 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7630 mapping_RND_to_EM[i] = Xalpha_quest;
7632 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7633 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7634 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7635 em_object_mapping_list[i].element_em;
7637 mapping_initialized = TRUE;
7640 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7641 return mapping_RND_to_EM[element_rnd];
7643 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7648 int map_element_EM_to_RND(int element_em)
7650 static unsigned short mapping_EM_to_RND[TILE_MAX];
7651 static boolean mapping_initialized = FALSE;
7653 if (!mapping_initialized)
7657 // return "EL_UNKNOWN" for all undefined elements in mapping array
7658 for (i = 0; i < TILE_MAX; i++)
7659 mapping_EM_to_RND[i] = EL_UNKNOWN;
7661 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7662 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7663 em_object_mapping_list[i].element_rnd;
7665 mapping_initialized = TRUE;
7668 if (element_em >= 0 && element_em < TILE_MAX)
7669 return mapping_EM_to_RND[element_em];
7671 Error(ERR_WARN, "invalid EM level element %d", element_em);
7676 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7678 struct LevelInfo_EM *level_em = level->native_em_level;
7679 struct LEVEL *lev = level_em->lev;
7682 for (i = 0; i < TILE_MAX; i++)
7683 lev->android_array[i] = Xblank;
7685 for (i = 0; i < level->num_android_clone_elements; i++)
7687 int element_rnd = level->android_clone_element[i];
7688 int element_em = map_element_RND_to_EM(element_rnd);
7690 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7691 if (em_object_mapping_list[j].element_rnd == element_rnd)
7692 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7696 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7698 struct LevelInfo_EM *level_em = level->native_em_level;
7699 struct LEVEL *lev = level_em->lev;
7702 level->num_android_clone_elements = 0;
7704 for (i = 0; i < TILE_MAX; i++)
7706 int element_em = lev->android_array[i];
7708 boolean element_found = FALSE;
7710 if (element_em == Xblank)
7713 element_rnd = map_element_EM_to_RND(element_em);
7715 for (j = 0; j < level->num_android_clone_elements; j++)
7716 if (level->android_clone_element[j] == element_rnd)
7717 element_found = TRUE;
7721 level->android_clone_element[level->num_android_clone_elements++] =
7724 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7729 if (level->num_android_clone_elements == 0)
7731 level->num_android_clone_elements = 1;
7732 level->android_clone_element[0] = EL_EMPTY;
7736 int map_direction_RND_to_EM(int direction)
7738 return (direction == MV_UP ? 0 :
7739 direction == MV_RIGHT ? 1 :
7740 direction == MV_DOWN ? 2 :
7741 direction == MV_LEFT ? 3 :
7745 int map_direction_EM_to_RND(int direction)
7747 return (direction == 0 ? MV_UP :
7748 direction == 1 ? MV_RIGHT :
7749 direction == 2 ? MV_DOWN :
7750 direction == 3 ? MV_LEFT :
7754 int map_element_RND_to_SP(int element_rnd)
7756 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7758 if (element_rnd >= EL_SP_START &&
7759 element_rnd <= EL_SP_END)
7760 element_sp = element_rnd - EL_SP_START;
7761 else if (element_rnd == EL_EMPTY_SPACE)
7763 else if (element_rnd == EL_INVISIBLE_WALL)
7769 int map_element_SP_to_RND(int element_sp)
7771 int element_rnd = EL_UNKNOWN;
7773 if (element_sp >= 0x00 &&
7775 element_rnd = EL_SP_START + element_sp;
7776 else if (element_sp == 0x28)
7777 element_rnd = EL_INVISIBLE_WALL;
7782 int map_action_SP_to_RND(int action_sp)
7786 case actActive: return ACTION_ACTIVE;
7787 case actImpact: return ACTION_IMPACT;
7788 case actExploding: return ACTION_EXPLODING;
7789 case actDigging: return ACTION_DIGGING;
7790 case actSnapping: return ACTION_SNAPPING;
7791 case actCollecting: return ACTION_COLLECTING;
7792 case actPassing: return ACTION_PASSING;
7793 case actPushing: return ACTION_PUSHING;
7794 case actDropping: return ACTION_DROPPING;
7796 default: return ACTION_DEFAULT;
7800 int map_element_RND_to_MM(int element_rnd)
7802 return (element_rnd >= EL_MM_START_1 &&
7803 element_rnd <= EL_MM_END_1 ?
7804 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7806 element_rnd >= EL_MM_START_2 &&
7807 element_rnd <= EL_MM_END_2 ?
7808 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7810 element_rnd >= EL_CHAR_START &&
7811 element_rnd <= EL_CHAR_END ?
7812 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7814 element_rnd >= EL_MM_RUNTIME_START &&
7815 element_rnd <= EL_MM_RUNTIME_END ?
7816 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7818 element_rnd >= EL_MM_DUMMY_START &&
7819 element_rnd <= EL_MM_DUMMY_END ?
7820 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7822 EL_MM_EMPTY_NATIVE);
7825 int map_element_MM_to_RND(int element_mm)
7827 return (element_mm == EL_MM_EMPTY_NATIVE ||
7828 element_mm == EL_DF_EMPTY_NATIVE ?
7831 element_mm >= EL_MM_START_1_NATIVE &&
7832 element_mm <= EL_MM_END_1_NATIVE ?
7833 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7835 element_mm >= EL_MM_START_2_NATIVE &&
7836 element_mm <= EL_MM_END_2_NATIVE ?
7837 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7839 element_mm >= EL_MM_CHAR_START_NATIVE &&
7840 element_mm <= EL_MM_CHAR_END_NATIVE ?
7841 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7843 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7844 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7845 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7847 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7848 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7849 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7854 int map_action_MM_to_RND(int action_mm)
7856 // all MM actions are defined to exactly match their RND counterparts
7860 int map_sound_MM_to_RND(int sound_mm)
7864 case SND_MM_GAME_LEVELTIME_CHARGING:
7865 return SND_GAME_LEVELTIME_CHARGING;
7867 case SND_MM_GAME_HEALTH_CHARGING:
7868 return SND_GAME_HEALTH_CHARGING;
7871 return SND_UNDEFINED;
7875 int map_mm_wall_element(int element)
7877 return (element >= EL_MM_STEEL_WALL_START &&
7878 element <= EL_MM_STEEL_WALL_END ?
7881 element >= EL_MM_WOODEN_WALL_START &&
7882 element <= EL_MM_WOODEN_WALL_END ?
7885 element >= EL_MM_ICE_WALL_START &&
7886 element <= EL_MM_ICE_WALL_END ?
7889 element >= EL_MM_AMOEBA_WALL_START &&
7890 element <= EL_MM_AMOEBA_WALL_END ?
7893 element >= EL_DF_STEEL_WALL_START &&
7894 element <= EL_DF_STEEL_WALL_END ?
7897 element >= EL_DF_WOODEN_WALL_START &&
7898 element <= EL_DF_WOODEN_WALL_END ?
7904 int map_mm_wall_element_editor(int element)
7908 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7909 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7910 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7911 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7912 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7913 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7915 default: return element;
7919 int get_next_element(int element)
7923 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7924 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7925 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7926 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7927 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7928 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7929 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7930 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7931 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7932 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7933 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7935 default: return element;
7939 int el2img_mm(int element_mm)
7941 return el2img(map_element_MM_to_RND(element_mm));
7944 int el_act_dir2img(int element, int action, int direction)
7946 element = GFX_ELEMENT(element);
7947 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7949 // direction_graphic[][] == graphic[] for undefined direction graphics
7950 return element_info[element].direction_graphic[action][direction];
7953 static int el_act_dir2crm(int element, int action, int direction)
7955 element = GFX_ELEMENT(element);
7956 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7958 // direction_graphic[][] == graphic[] for undefined direction graphics
7959 return element_info[element].direction_crumbled[action][direction];
7962 int el_act2img(int element, int action)
7964 element = GFX_ELEMENT(element);
7966 return element_info[element].graphic[action];
7969 int el_act2crm(int element, int action)
7971 element = GFX_ELEMENT(element);
7973 return element_info[element].crumbled[action];
7976 int el_dir2img(int element, int direction)
7978 element = GFX_ELEMENT(element);
7980 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7983 int el2baseimg(int element)
7985 return element_info[element].graphic[ACTION_DEFAULT];
7988 int el2img(int element)
7990 element = GFX_ELEMENT(element);
7992 return element_info[element].graphic[ACTION_DEFAULT];
7995 int el2edimg(int element)
7997 element = GFX_ELEMENT(element);
7999 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8002 int el2preimg(int element)
8004 element = GFX_ELEMENT(element);
8006 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8009 int el2panelimg(int element)
8011 element = GFX_ELEMENT(element);
8013 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8016 int font2baseimg(int font_nr)
8018 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8021 int getBeltNrFromBeltElement(int element)
8023 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8024 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8025 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8028 int getBeltNrFromBeltActiveElement(int element)
8030 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8031 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8032 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8035 int getBeltNrFromBeltSwitchElement(int element)
8037 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8038 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8039 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8042 int getBeltDirNrFromBeltElement(int element)
8044 static int belt_base_element[4] =
8046 EL_CONVEYOR_BELT_1_LEFT,
8047 EL_CONVEYOR_BELT_2_LEFT,
8048 EL_CONVEYOR_BELT_3_LEFT,
8049 EL_CONVEYOR_BELT_4_LEFT
8052 int belt_nr = getBeltNrFromBeltElement(element);
8053 int belt_dir_nr = element - belt_base_element[belt_nr];
8055 return (belt_dir_nr % 3);
8058 int getBeltDirNrFromBeltSwitchElement(int element)
8060 static int belt_base_element[4] =
8062 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8063 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8064 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8065 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8068 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8069 int belt_dir_nr = element - belt_base_element[belt_nr];
8071 return (belt_dir_nr % 3);
8074 int getBeltDirFromBeltElement(int element)
8076 static int belt_move_dir[3] =
8083 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8085 return belt_move_dir[belt_dir_nr];
8088 int getBeltDirFromBeltSwitchElement(int element)
8090 static int belt_move_dir[3] =
8097 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8099 return belt_move_dir[belt_dir_nr];
8102 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8104 static int belt_base_element[4] =
8106 EL_CONVEYOR_BELT_1_LEFT,
8107 EL_CONVEYOR_BELT_2_LEFT,
8108 EL_CONVEYOR_BELT_3_LEFT,
8109 EL_CONVEYOR_BELT_4_LEFT
8112 return belt_base_element[belt_nr] + belt_dir_nr;
8115 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8117 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8119 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8122 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8124 static int belt_base_element[4] =
8126 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8127 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8128 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8129 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8132 return belt_base_element[belt_nr] + belt_dir_nr;
8135 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8137 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8139 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8142 boolean getTeamMode_EM(void)
8144 return game.team_mode || network_playing;
8147 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8149 int game_frame_delay_value;
8151 game_frame_delay_value =
8152 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8153 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8156 if (tape.playing && tape.warp_forward && !tape.pausing)
8157 game_frame_delay_value = 0;
8159 return game_frame_delay_value;
8162 unsigned int InitRND(int seed)
8164 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8165 return InitEngineRandom_EM(seed);
8166 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8167 return InitEngineRandom_SP(seed);
8168 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8169 return InitEngineRandom_MM(seed);
8171 return InitEngineRandom_RND(seed);
8174 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8175 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8177 static int get_effective_element_EM(int tile, int frame_em)
8179 int element = object_mapping[tile].element_rnd;
8180 int action = object_mapping[tile].action;
8181 boolean is_backside = object_mapping[tile].is_backside;
8182 boolean action_removing = (action == ACTION_DIGGING ||
8183 action == ACTION_SNAPPING ||
8184 action == ACTION_COLLECTING);
8190 case Yacid_splash_eB:
8191 case Yacid_splash_wB:
8192 return (frame_em > 5 ? EL_EMPTY : element);
8198 else // frame_em == 7
8202 case Yacid_splash_eB:
8203 case Yacid_splash_wB:
8206 case Yemerald_stone:
8209 case Ydiamond_stone:
8213 case Xdrip_stretchB:
8232 case Xsand_stonein_1:
8233 case Xsand_stonein_2:
8234 case Xsand_stonein_3:
8235 case Xsand_stonein_4:
8239 return (is_backside || action_removing ? EL_EMPTY : element);
8244 static boolean check_linear_animation_EM(int tile)
8248 case Xsand_stonesand_1:
8249 case Xsand_stonesand_quickout_1:
8250 case Xsand_sandstone_1:
8251 case Xsand_stonein_1:
8252 case Xsand_stoneout_1:
8271 case Yacid_splash_eB:
8272 case Yacid_splash_wB:
8273 case Yemerald_stone:
8280 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8281 boolean has_crumbled_graphics,
8282 int crumbled, int sync_frame)
8284 // if element can be crumbled, but certain action graphics are just empty
8285 // space (like instantly snapping sand to empty space in 1 frame), do not
8286 // treat these empty space graphics as crumbled graphics in EMC engine
8287 if (crumbled == IMG_EMPTY_SPACE)
8288 has_crumbled_graphics = FALSE;
8290 if (has_crumbled_graphics)
8292 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8293 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8294 g_crumbled->anim_delay,
8295 g_crumbled->anim_mode,
8296 g_crumbled->anim_start_frame,
8299 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8300 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8302 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8303 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8305 g_em->has_crumbled_graphics = TRUE;
8309 g_em->crumbled_bitmap = NULL;
8310 g_em->crumbled_src_x = 0;
8311 g_em->crumbled_src_y = 0;
8312 g_em->crumbled_border_size = 0;
8313 g_em->crumbled_tile_size = 0;
8315 g_em->has_crumbled_graphics = FALSE;
8320 void ResetGfxAnimation_EM(int x, int y, int tile)
8326 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8327 int tile, int frame_em, int x, int y)
8329 int action = object_mapping[tile].action;
8330 int direction = object_mapping[tile].direction;
8331 int effective_element = get_effective_element_EM(tile, frame_em);
8332 int graphic = (direction == MV_NONE ?
8333 el_act2img(effective_element, action) :
8334 el_act_dir2img(effective_element, action, direction));
8335 struct GraphicInfo *g = &graphic_info[graphic];
8337 boolean action_removing = (action == ACTION_DIGGING ||
8338 action == ACTION_SNAPPING ||
8339 action == ACTION_COLLECTING);
8340 boolean action_moving = (action == ACTION_FALLING ||
8341 action == ACTION_MOVING ||
8342 action == ACTION_PUSHING ||
8343 action == ACTION_EATING ||
8344 action == ACTION_FILLING ||
8345 action == ACTION_EMPTYING);
8346 boolean action_falling = (action == ACTION_FALLING ||
8347 action == ACTION_FILLING ||
8348 action == ACTION_EMPTYING);
8350 // special case: graphic uses "2nd movement tile" and has defined
8351 // 7 frames for movement animation (or less) => use default graphic
8352 // for last (8th) frame which ends the movement animation
8353 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8355 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8356 graphic = (direction == MV_NONE ?
8357 el_act2img(effective_element, action) :
8358 el_act_dir2img(effective_element, action, direction));
8360 g = &graphic_info[graphic];
8363 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8367 else if (action_moving)
8369 boolean is_backside = object_mapping[tile].is_backside;
8373 int direction = object_mapping[tile].direction;
8374 int move_dir = (action_falling ? MV_DOWN : direction);
8379 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8380 if (g->double_movement && frame_em == 0)
8384 if (move_dir == MV_LEFT)
8385 GfxFrame[x - 1][y] = GfxFrame[x][y];
8386 else if (move_dir == MV_RIGHT)
8387 GfxFrame[x + 1][y] = GfxFrame[x][y];
8388 else if (move_dir == MV_UP)
8389 GfxFrame[x][y - 1] = GfxFrame[x][y];
8390 else if (move_dir == MV_DOWN)
8391 GfxFrame[x][y + 1] = GfxFrame[x][y];
8398 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8399 if (tile == Xsand_stonesand_quickout_1 ||
8400 tile == Xsand_stonesand_quickout_2)
8404 if (graphic_info[graphic].anim_global_sync)
8405 sync_frame = FrameCounter;
8406 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8407 sync_frame = GfxFrame[x][y];
8409 sync_frame = 0; // playfield border (pseudo steel)
8411 SetRandomAnimationValue(x, y);
8413 int frame = getAnimationFrame(g->anim_frames,
8416 g->anim_start_frame,
8419 g_em->unique_identifier =
8420 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8423 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8424 int tile, int frame_em, int x, int y)
8426 int action = object_mapping[tile].action;
8427 int direction = object_mapping[tile].direction;
8428 boolean is_backside = object_mapping[tile].is_backside;
8429 int effective_element = get_effective_element_EM(tile, frame_em);
8430 int effective_action = action;
8431 int graphic = (direction == MV_NONE ?
8432 el_act2img(effective_element, effective_action) :
8433 el_act_dir2img(effective_element, effective_action,
8435 int crumbled = (direction == MV_NONE ?
8436 el_act2crm(effective_element, effective_action) :
8437 el_act_dir2crm(effective_element, effective_action,
8439 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8440 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8441 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8442 struct GraphicInfo *g = &graphic_info[graphic];
8445 // special case: graphic uses "2nd movement tile" and has defined
8446 // 7 frames for movement animation (or less) => use default graphic
8447 // for last (8th) frame which ends the movement animation
8448 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8450 effective_action = ACTION_DEFAULT;
8451 graphic = (direction == MV_NONE ?
8452 el_act2img(effective_element, effective_action) :
8453 el_act_dir2img(effective_element, effective_action,
8455 crumbled = (direction == MV_NONE ?
8456 el_act2crm(effective_element, effective_action) :
8457 el_act_dir2crm(effective_element, effective_action,
8460 g = &graphic_info[graphic];
8463 if (graphic_info[graphic].anim_global_sync)
8464 sync_frame = FrameCounter;
8465 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8466 sync_frame = GfxFrame[x][y];
8468 sync_frame = 0; // playfield border (pseudo steel)
8470 SetRandomAnimationValue(x, y);
8472 int frame = getAnimationFrame(g->anim_frames,
8475 g->anim_start_frame,
8478 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8479 g->double_movement && is_backside);
8481 // (updating the "crumbled" graphic definitions is probably not really needed,
8482 // as animations for crumbled graphics can't be longer than one EMC cycle)
8483 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8487 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8488 int player_nr, int anim, int frame_em)
8490 int element = player_mapping[player_nr][anim].element_rnd;
8491 int action = player_mapping[player_nr][anim].action;
8492 int direction = player_mapping[player_nr][anim].direction;
8493 int graphic = (direction == MV_NONE ?
8494 el_act2img(element, action) :
8495 el_act_dir2img(element, action, direction));
8496 struct GraphicInfo *g = &graphic_info[graphic];
8499 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8501 stored_player[player_nr].StepFrame = frame_em;
8503 sync_frame = stored_player[player_nr].Frame;
8505 int frame = getAnimationFrame(g->anim_frames,
8508 g->anim_start_frame,
8511 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8512 &g_em->src_x, &g_em->src_y, FALSE);
8515 void InitGraphicInfo_EM(void)
8520 int num_em_gfx_errors = 0;
8522 if (graphic_info_em_object[0][0].bitmap == NULL)
8524 // EM graphics not yet initialized in em_open_all()
8529 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8532 // always start with reliable default values
8533 for (i = 0; i < TILE_MAX; i++)
8535 object_mapping[i].element_rnd = EL_UNKNOWN;
8536 object_mapping[i].is_backside = FALSE;
8537 object_mapping[i].action = ACTION_DEFAULT;
8538 object_mapping[i].direction = MV_NONE;
8541 // always start with reliable default values
8542 for (p = 0; p < MAX_PLAYERS; p++)
8544 for (i = 0; i < SPR_MAX; i++)
8546 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8547 player_mapping[p][i].action = ACTION_DEFAULT;
8548 player_mapping[p][i].direction = MV_NONE;
8552 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8554 int e = em_object_mapping_list[i].element_em;
8556 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8557 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8559 if (em_object_mapping_list[i].action != -1)
8560 object_mapping[e].action = em_object_mapping_list[i].action;
8562 if (em_object_mapping_list[i].direction != -1)
8563 object_mapping[e].direction =
8564 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8567 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8569 int a = em_player_mapping_list[i].action_em;
8570 int p = em_player_mapping_list[i].player_nr;
8572 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8574 if (em_player_mapping_list[i].action != -1)
8575 player_mapping[p][a].action = em_player_mapping_list[i].action;
8577 if (em_player_mapping_list[i].direction != -1)
8578 player_mapping[p][a].direction =
8579 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8582 for (i = 0; i < TILE_MAX; i++)
8584 int element = object_mapping[i].element_rnd;
8585 int action = object_mapping[i].action;
8586 int direction = object_mapping[i].direction;
8587 boolean is_backside = object_mapping[i].is_backside;
8588 boolean action_exploding = ((action == ACTION_EXPLODING ||
8589 action == ACTION_SMASHED_BY_ROCK ||
8590 action == ACTION_SMASHED_BY_SPRING) &&
8591 element != EL_DIAMOND);
8592 boolean action_active = (action == ACTION_ACTIVE);
8593 boolean action_other = (action == ACTION_OTHER);
8595 for (j = 0; j < 8; j++)
8597 int effective_element = get_effective_element_EM(i, j);
8598 int effective_action = (j < 7 ? action :
8599 i == Xdrip_stretch ? action :
8600 i == Xdrip_stretchB ? action :
8601 i == Ydrip_s1 ? action :
8602 i == Ydrip_s1B ? action :
8603 i == Xball_1B ? action :
8604 i == Xball_2 ? action :
8605 i == Xball_2B ? action :
8606 i == Yball_eat ? action :
8607 i == Ykey_1_eat ? action :
8608 i == Ykey_2_eat ? action :
8609 i == Ykey_3_eat ? action :
8610 i == Ykey_4_eat ? action :
8611 i == Ykey_5_eat ? action :
8612 i == Ykey_6_eat ? action :
8613 i == Ykey_7_eat ? action :
8614 i == Ykey_8_eat ? action :
8615 i == Ylenses_eat ? action :
8616 i == Ymagnify_eat ? action :
8617 i == Ygrass_eat ? action :
8618 i == Ydirt_eat ? action :
8619 i == Xsand_stonein_1 ? action :
8620 i == Xsand_stonein_2 ? action :
8621 i == Xsand_stonein_3 ? action :
8622 i == Xsand_stonein_4 ? action :
8623 i == Xsand_stoneout_1 ? action :
8624 i == Xsand_stoneout_2 ? action :
8625 i == Xboom_android ? ACTION_EXPLODING :
8626 action_exploding ? ACTION_EXPLODING :
8627 action_active ? action :
8628 action_other ? action :
8630 int graphic = (el_act_dir2img(effective_element, effective_action,
8632 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8634 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8635 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8636 boolean has_action_graphics = (graphic != base_graphic);
8637 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8638 struct GraphicInfo *g = &graphic_info[graphic];
8639 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8642 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8643 boolean special_animation = (action != ACTION_DEFAULT &&
8644 g->anim_frames == 3 &&
8645 g->anim_delay == 2 &&
8646 g->anim_mode & ANIM_LINEAR);
8647 int sync_frame = (i == Xdrip_stretch ? 7 :
8648 i == Xdrip_stretchB ? 7 :
8649 i == Ydrip_s2 ? j + 8 :
8650 i == Ydrip_s2B ? j + 8 :
8659 i == Xfake_acid_1 ? 0 :
8660 i == Xfake_acid_2 ? 10 :
8661 i == Xfake_acid_3 ? 20 :
8662 i == Xfake_acid_4 ? 30 :
8663 i == Xfake_acid_5 ? 40 :
8664 i == Xfake_acid_6 ? 50 :
8665 i == Xfake_acid_7 ? 60 :
8666 i == Xfake_acid_8 ? 70 :
8668 i == Xball_2B ? j + 8 :
8669 i == Yball_eat ? j + 1 :
8670 i == Ykey_1_eat ? j + 1 :
8671 i == Ykey_2_eat ? j + 1 :
8672 i == Ykey_3_eat ? j + 1 :
8673 i == Ykey_4_eat ? j + 1 :
8674 i == Ykey_5_eat ? j + 1 :
8675 i == Ykey_6_eat ? j + 1 :
8676 i == Ykey_7_eat ? j + 1 :
8677 i == Ykey_8_eat ? j + 1 :
8678 i == Ylenses_eat ? j + 1 :
8679 i == Ymagnify_eat ? j + 1 :
8680 i == Ygrass_eat ? j + 1 :
8681 i == Ydirt_eat ? j + 1 :
8682 i == Xamoeba_1 ? 0 :
8683 i == Xamoeba_2 ? 1 :
8684 i == Xamoeba_3 ? 2 :
8685 i == Xamoeba_4 ? 3 :
8686 i == Xamoeba_5 ? 0 :
8687 i == Xamoeba_6 ? 1 :
8688 i == Xamoeba_7 ? 2 :
8689 i == Xamoeba_8 ? 3 :
8690 i == Xexit_2 ? j + 8 :
8691 i == Xexit_3 ? j + 16 :
8692 i == Xdynamite_1 ? 0 :
8693 i == Xdynamite_2 ? 8 :
8694 i == Xdynamite_3 ? 16 :
8695 i == Xdynamite_4 ? 24 :
8696 i == Xsand_stonein_1 ? j + 1 :
8697 i == Xsand_stonein_2 ? j + 9 :
8698 i == Xsand_stonein_3 ? j + 17 :
8699 i == Xsand_stonein_4 ? j + 25 :
8700 i == Xsand_stoneout_1 && j == 0 ? 0 :
8701 i == Xsand_stoneout_1 && j == 1 ? 0 :
8702 i == Xsand_stoneout_1 && j == 2 ? 1 :
8703 i == Xsand_stoneout_1 && j == 3 ? 2 :
8704 i == Xsand_stoneout_1 && j == 4 ? 2 :
8705 i == Xsand_stoneout_1 && j == 5 ? 3 :
8706 i == Xsand_stoneout_1 && j == 6 ? 4 :
8707 i == Xsand_stoneout_1 && j == 7 ? 4 :
8708 i == Xsand_stoneout_2 && j == 0 ? 5 :
8709 i == Xsand_stoneout_2 && j == 1 ? 6 :
8710 i == Xsand_stoneout_2 && j == 2 ? 7 :
8711 i == Xsand_stoneout_2 && j == 3 ? 8 :
8712 i == Xsand_stoneout_2 && j == 4 ? 9 :
8713 i == Xsand_stoneout_2 && j == 5 ? 11 :
8714 i == Xsand_stoneout_2 && j == 6 ? 13 :
8715 i == Xsand_stoneout_2 && j == 7 ? 15 :
8716 i == Xboom_bug && j == 1 ? 2 :
8717 i == Xboom_bug && j == 2 ? 2 :
8718 i == Xboom_bug && j == 3 ? 4 :
8719 i == Xboom_bug && j == 4 ? 4 :
8720 i == Xboom_bug && j == 5 ? 2 :
8721 i == Xboom_bug && j == 6 ? 2 :
8722 i == Xboom_bug && j == 7 ? 0 :
8723 i == Xboom_bomb && j == 1 ? 2 :
8724 i == Xboom_bomb && j == 2 ? 2 :
8725 i == Xboom_bomb && j == 3 ? 4 :
8726 i == Xboom_bomb && j == 4 ? 4 :
8727 i == Xboom_bomb && j == 5 ? 2 :
8728 i == Xboom_bomb && j == 6 ? 2 :
8729 i == Xboom_bomb && j == 7 ? 0 :
8730 i == Xboom_android && j == 7 ? 6 :
8731 i == Xboom_1 && j == 1 ? 2 :
8732 i == Xboom_1 && j == 2 ? 2 :
8733 i == Xboom_1 && j == 3 ? 4 :
8734 i == Xboom_1 && j == 4 ? 4 :
8735 i == Xboom_1 && j == 5 ? 6 :
8736 i == Xboom_1 && j == 6 ? 6 :
8737 i == Xboom_1 && j == 7 ? 8 :
8738 i == Xboom_2 && j == 0 ? 8 :
8739 i == Xboom_2 && j == 1 ? 8 :
8740 i == Xboom_2 && j == 2 ? 10 :
8741 i == Xboom_2 && j == 3 ? 10 :
8742 i == Xboom_2 && j == 4 ? 10 :
8743 i == Xboom_2 && j == 5 ? 12 :
8744 i == Xboom_2 && j == 6 ? 12 :
8745 i == Xboom_2 && j == 7 ? 12 :
8746 special_animation && j == 4 ? 3 :
8747 effective_action != action ? 0 :
8751 Bitmap *debug_bitmap = g_em->bitmap;
8752 int debug_src_x = g_em->src_x;
8753 int debug_src_y = g_em->src_y;
8756 int frame = getAnimationFrame(g->anim_frames,
8759 g->anim_start_frame,
8762 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8763 g->double_movement && is_backside);
8765 g_em->bitmap = src_bitmap;
8766 g_em->src_x = src_x;
8767 g_em->src_y = src_y;
8768 g_em->src_offset_x = 0;
8769 g_em->src_offset_y = 0;
8770 g_em->dst_offset_x = 0;
8771 g_em->dst_offset_y = 0;
8772 g_em->width = TILEX;
8773 g_em->height = TILEY;
8775 g_em->preserve_background = FALSE;
8777 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8780 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8781 effective_action == ACTION_MOVING ||
8782 effective_action == ACTION_PUSHING ||
8783 effective_action == ACTION_EATING)) ||
8784 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8785 effective_action == ACTION_EMPTYING)))
8788 (effective_action == ACTION_FALLING ||
8789 effective_action == ACTION_FILLING ||
8790 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8791 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8792 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8793 int num_steps = (i == Ydrip_s1 ? 16 :
8794 i == Ydrip_s1B ? 16 :
8795 i == Ydrip_s2 ? 16 :
8796 i == Ydrip_s2B ? 16 :
8797 i == Xsand_stonein_1 ? 32 :
8798 i == Xsand_stonein_2 ? 32 :
8799 i == Xsand_stonein_3 ? 32 :
8800 i == Xsand_stonein_4 ? 32 :
8801 i == Xsand_stoneout_1 ? 16 :
8802 i == Xsand_stoneout_2 ? 16 : 8);
8803 int cx = ABS(dx) * (TILEX / num_steps);
8804 int cy = ABS(dy) * (TILEY / num_steps);
8805 int step_frame = (i == Ydrip_s2 ? j + 8 :
8806 i == Ydrip_s2B ? j + 8 :
8807 i == Xsand_stonein_2 ? j + 8 :
8808 i == Xsand_stonein_3 ? j + 16 :
8809 i == Xsand_stonein_4 ? j + 24 :
8810 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8811 int step = (is_backside ? step_frame : num_steps - step_frame);
8813 if (is_backside) // tile where movement starts
8815 if (dx < 0 || dy < 0)
8817 g_em->src_offset_x = cx * step;
8818 g_em->src_offset_y = cy * step;
8822 g_em->dst_offset_x = cx * step;
8823 g_em->dst_offset_y = cy * step;
8826 else // tile where movement ends
8828 if (dx < 0 || dy < 0)
8830 g_em->dst_offset_x = cx * step;
8831 g_em->dst_offset_y = cy * step;
8835 g_em->src_offset_x = cx * step;
8836 g_em->src_offset_y = cy * step;
8840 g_em->width = TILEX - cx * step;
8841 g_em->height = TILEY - cy * step;
8844 // create unique graphic identifier to decide if tile must be redrawn
8845 /* bit 31 - 16 (16 bit): EM style graphic
8846 bit 15 - 12 ( 4 bit): EM style frame
8847 bit 11 - 6 ( 6 bit): graphic width
8848 bit 5 - 0 ( 6 bit): graphic height */
8849 g_em->unique_identifier =
8850 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8854 // skip check for EMC elements not contained in original EMC artwork
8855 if (element == EL_EMC_FAKE_ACID)
8858 if (g_em->bitmap != debug_bitmap ||
8859 g_em->src_x != debug_src_x ||
8860 g_em->src_y != debug_src_y ||
8861 g_em->src_offset_x != 0 ||
8862 g_em->src_offset_y != 0 ||
8863 g_em->dst_offset_x != 0 ||
8864 g_em->dst_offset_y != 0 ||
8865 g_em->width != TILEX ||
8866 g_em->height != TILEY)
8868 static int last_i = -1;
8876 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8877 i, element, element_info[element].token_name,
8878 element_action_info[effective_action].suffix, direction);
8880 if (element != effective_element)
8881 printf(" [%d ('%s')]",
8883 element_info[effective_element].token_name);
8887 if (g_em->bitmap != debug_bitmap)
8888 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8889 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8891 if (g_em->src_x != debug_src_x ||
8892 g_em->src_y != debug_src_y)
8893 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8894 j, (is_backside ? 'B' : 'F'),
8895 g_em->src_x, g_em->src_y,
8896 g_em->src_x / 32, g_em->src_y / 32,
8897 debug_src_x, debug_src_y,
8898 debug_src_x / 32, debug_src_y / 32);
8900 if (g_em->src_offset_x != 0 ||
8901 g_em->src_offset_y != 0 ||
8902 g_em->dst_offset_x != 0 ||
8903 g_em->dst_offset_y != 0)
8904 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8906 g_em->src_offset_x, g_em->src_offset_y,
8907 g_em->dst_offset_x, g_em->dst_offset_y);
8909 if (g_em->width != TILEX ||
8910 g_em->height != TILEY)
8911 printf(" %d (%d): size %d,%d should be %d,%d\n",
8913 g_em->width, g_em->height, TILEX, TILEY);
8915 num_em_gfx_errors++;
8922 for (i = 0; i < TILE_MAX; i++)
8924 for (j = 0; j < 8; j++)
8926 int element = object_mapping[i].element_rnd;
8927 int action = object_mapping[i].action;
8928 int direction = object_mapping[i].direction;
8929 boolean is_backside = object_mapping[i].is_backside;
8930 int graphic_action = el_act_dir2img(element, action, direction);
8931 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8933 if ((action == ACTION_SMASHED_BY_ROCK ||
8934 action == ACTION_SMASHED_BY_SPRING ||
8935 action == ACTION_EATING) &&
8936 graphic_action == graphic_default)
8938 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8939 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8940 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8941 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8944 // no separate animation for "smashed by rock" -- use rock instead
8945 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8946 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8948 g_em->bitmap = g_xx->bitmap;
8949 g_em->src_x = g_xx->src_x;
8950 g_em->src_y = g_xx->src_y;
8951 g_em->src_offset_x = g_xx->src_offset_x;
8952 g_em->src_offset_y = g_xx->src_offset_y;
8953 g_em->dst_offset_x = g_xx->dst_offset_x;
8954 g_em->dst_offset_y = g_xx->dst_offset_y;
8955 g_em->width = g_xx->width;
8956 g_em->height = g_xx->height;
8957 g_em->unique_identifier = g_xx->unique_identifier;
8960 g_em->preserve_background = TRUE;
8965 for (p = 0; p < MAX_PLAYERS; p++)
8967 for (i = 0; i < SPR_MAX; i++)
8969 int element = player_mapping[p][i].element_rnd;
8970 int action = player_mapping[p][i].action;
8971 int direction = player_mapping[p][i].direction;
8973 for (j = 0; j < 8; j++)
8975 int effective_element = element;
8976 int effective_action = action;
8977 int graphic = (direction == MV_NONE ?
8978 el_act2img(effective_element, effective_action) :
8979 el_act_dir2img(effective_element, effective_action,
8981 struct GraphicInfo *g = &graphic_info[graphic];
8982 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8988 Bitmap *debug_bitmap = g_em->bitmap;
8989 int debug_src_x = g_em->src_x;
8990 int debug_src_y = g_em->src_y;
8993 int frame = getAnimationFrame(g->anim_frames,
8996 g->anim_start_frame,
8999 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9001 g_em->bitmap = src_bitmap;
9002 g_em->src_x = src_x;
9003 g_em->src_y = src_y;
9004 g_em->src_offset_x = 0;
9005 g_em->src_offset_y = 0;
9006 g_em->dst_offset_x = 0;
9007 g_em->dst_offset_y = 0;
9008 g_em->width = TILEX;
9009 g_em->height = TILEY;
9013 // skip check for EMC elements not contained in original EMC artwork
9014 if (element == EL_PLAYER_3 ||
9015 element == EL_PLAYER_4)
9018 if (g_em->bitmap != debug_bitmap ||
9019 g_em->src_x != debug_src_x ||
9020 g_em->src_y != debug_src_y)
9022 static int last_i = -1;
9030 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9031 p, i, element, element_info[element].token_name,
9032 element_action_info[effective_action].suffix, direction);
9034 if (element != effective_element)
9035 printf(" [%d ('%s')]",
9037 element_info[effective_element].token_name);
9041 if (g_em->bitmap != debug_bitmap)
9042 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9043 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9045 if (g_em->src_x != debug_src_x ||
9046 g_em->src_y != debug_src_y)
9047 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9049 g_em->src_x, g_em->src_y,
9050 g_em->src_x / 32, g_em->src_y / 32,
9051 debug_src_x, debug_src_y,
9052 debug_src_x / 32, debug_src_y / 32);
9054 num_em_gfx_errors++;
9064 printf("::: [%d errors found]\n", num_em_gfx_errors);
9070 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9071 boolean any_player_moving,
9072 boolean any_player_snapping,
9073 boolean any_player_dropping)
9075 if (frame == 0 && !any_player_dropping)
9077 if (!local_player->was_waiting)
9079 if (!CheckSaveEngineSnapshotToList())
9082 local_player->was_waiting = TRUE;
9085 else if (any_player_moving || any_player_snapping || any_player_dropping)
9087 local_player->was_waiting = FALSE;
9091 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9092 boolean murphy_is_dropping)
9094 if (murphy_is_waiting)
9096 if (!local_player->was_waiting)
9098 if (!CheckSaveEngineSnapshotToList())
9101 local_player->was_waiting = TRUE;
9106 local_player->was_waiting = FALSE;
9110 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9111 boolean button_released)
9113 if (button_released)
9115 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9116 CheckSaveEngineSnapshotToList();
9118 else if (element_clicked)
9120 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9121 CheckSaveEngineSnapshotToList();
9123 game.snapshot.changed_action = TRUE;
9127 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9128 boolean any_player_moving,
9129 boolean any_player_snapping,
9130 boolean any_player_dropping)
9132 if (tape.single_step && tape.recording && !tape.pausing)
9133 if (frame == 0 && !any_player_dropping)
9134 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9136 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9137 any_player_snapping, any_player_dropping);
9140 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9141 boolean murphy_is_dropping)
9143 boolean murphy_starts_dropping = FALSE;
9146 for (i = 0; i < MAX_PLAYERS; i++)
9147 if (stored_player[i].force_dropping)
9148 murphy_starts_dropping = TRUE;
9150 if (tape.single_step && tape.recording && !tape.pausing)
9151 if (murphy_is_waiting && !murphy_starts_dropping)
9152 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9154 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9157 void CheckSingleStepMode_MM(boolean element_clicked,
9158 boolean button_released)
9160 if (tape.single_step && tape.recording && !tape.pausing)
9161 if (button_released)
9162 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9164 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9167 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9168 int graphic, int sync_frame, int x, int y)
9170 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9172 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9175 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9177 return (IS_NEXT_FRAME(sync_frame, graphic));
9180 int getGraphicInfo_Delay(int graphic)
9182 return graphic_info[graphic].anim_delay;
9185 void PlayMenuSoundExt(int sound)
9187 if (sound == SND_UNDEFINED)
9190 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9191 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9194 if (IS_LOOP_SOUND(sound))
9195 PlaySoundLoop(sound);
9200 void PlayMenuSound(void)
9202 PlayMenuSoundExt(menu.sound[game_status]);
9205 void PlayMenuSoundStereo(int sound, int stereo_position)
9207 if (sound == SND_UNDEFINED)
9210 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9211 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9214 if (IS_LOOP_SOUND(sound))
9215 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9217 PlaySoundStereo(sound, stereo_position);
9220 void PlayMenuSoundIfLoopExt(int sound)
9222 if (sound == SND_UNDEFINED)
9225 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9226 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9229 if (IS_LOOP_SOUND(sound))
9230 PlaySoundLoop(sound);
9233 void PlayMenuSoundIfLoop(void)
9235 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9238 void PlayMenuMusicExt(int music)
9240 if (music == MUS_UNDEFINED)
9243 if (!setup.sound_music)
9246 if (IS_LOOP_MUSIC(music))
9247 PlayMusicLoop(music);
9252 void PlayMenuMusic(void)
9254 char *curr_music = getCurrentlyPlayingMusicFilename();
9255 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9257 if (!strEqual(curr_music, next_music))
9258 PlayMenuMusicExt(menu.music[game_status]);
9261 void PlayMenuSoundsAndMusic(void)
9267 static void FadeMenuSounds(void)
9272 static void FadeMenuMusic(void)
9274 char *curr_music = getCurrentlyPlayingMusicFilename();
9275 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9277 if (!strEqual(curr_music, next_music))
9281 void FadeMenuSoundsAndMusic(void)
9287 void PlaySoundActivating(void)
9290 PlaySound(SND_MENU_ITEM_ACTIVATING);
9294 void PlaySoundSelecting(void)
9297 PlaySound(SND_MENU_ITEM_SELECTING);
9301 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9303 boolean change_fullscreen = (setup.fullscreen !=
9304 video.fullscreen_enabled);
9305 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9306 setup.window_scaling_percent !=
9307 video.window_scaling_percent);
9309 if (change_window_scaling_percent && video.fullscreen_enabled)
9312 if (!change_window_scaling_percent && !video.fullscreen_available)
9315 if (change_window_scaling_percent)
9317 SDLSetWindowScaling(setup.window_scaling_percent);
9321 else if (change_fullscreen)
9323 SDLSetWindowFullscreen(setup.fullscreen);
9325 // set setup value according to successfully changed fullscreen mode
9326 setup.fullscreen = video.fullscreen_enabled;
9331 if (change_fullscreen ||
9332 change_window_scaling_percent)
9334 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9336 // save backbuffer content which gets lost when toggling fullscreen mode
9337 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9339 if (change_window_scaling_percent)
9341 // keep window mode, but change window scaling
9342 video.fullscreen_enabled = TRUE; // force new window scaling
9345 // toggle fullscreen
9346 ChangeVideoModeIfNeeded(setup.fullscreen);
9348 // set setup value according to successfully changed fullscreen mode
9349 setup.fullscreen = video.fullscreen_enabled;
9351 // restore backbuffer content from temporary backbuffer backup bitmap
9352 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9354 FreeBitmap(tmp_backbuffer);
9356 // update visible window/screen
9357 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9361 static void JoinRectangles(int *x, int *y, int *width, int *height,
9362 int x2, int y2, int width2, int height2)
9364 // do not join with "off-screen" rectangle
9365 if (x2 == -1 || y2 == -1)
9370 *width = MAX(*width, width2);
9371 *height = MAX(*height, height2);
9374 void SetAnimStatus(int anim_status_new)
9376 if (anim_status_new == GAME_MODE_MAIN)
9377 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9378 else if (anim_status_new == GAME_MODE_SCORES)
9379 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9381 global.anim_status_next = anim_status_new;
9383 // directly set screen modes that are entered without fading
9384 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9385 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9386 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9387 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9388 global.anim_status = global.anim_status_next;
9391 void SetGameStatus(int game_status_new)
9393 if (game_status_new != game_status)
9394 game_status_last_screen = game_status;
9396 game_status = game_status_new;
9398 SetAnimStatus(game_status_new);
9401 void SetFontStatus(int game_status_new)
9403 static int last_game_status = -1;
9405 if (game_status_new != -1)
9407 // set game status for font use after storing last game status
9408 last_game_status = game_status;
9409 game_status = game_status_new;
9413 // reset game status after font use from last stored game status
9414 game_status = last_game_status;
9418 void ResetFontStatus(void)
9423 void SetLevelSetInfo(char *identifier, int level_nr)
9425 setString(&levelset.identifier, identifier);
9427 levelset.level_nr = level_nr;
9430 boolean CheckIfAllViewportsHaveChanged(void)
9432 // if game status has not changed, viewports have not changed either
9433 if (game_status == game_status_last)
9436 // check if all viewports have changed with current game status
9438 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9439 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9440 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9441 int new_real_sx = vp_playfield->x;
9442 int new_real_sy = vp_playfield->y;
9443 int new_full_sxsize = vp_playfield->width;
9444 int new_full_sysize = vp_playfield->height;
9445 int new_dx = vp_door_1->x;
9446 int new_dy = vp_door_1->y;
9447 int new_dxsize = vp_door_1->width;
9448 int new_dysize = vp_door_1->height;
9449 int new_vx = vp_door_2->x;
9450 int new_vy = vp_door_2->y;
9451 int new_vxsize = vp_door_2->width;
9452 int new_vysize = vp_door_2->height;
9454 boolean playfield_viewport_has_changed =
9455 (new_real_sx != REAL_SX ||
9456 new_real_sy != REAL_SY ||
9457 new_full_sxsize != FULL_SXSIZE ||
9458 new_full_sysize != FULL_SYSIZE);
9460 boolean door_1_viewport_has_changed =
9463 new_dxsize != DXSIZE ||
9464 new_dysize != DYSIZE);
9466 boolean door_2_viewport_has_changed =
9469 new_vxsize != VXSIZE ||
9470 new_vysize != VYSIZE ||
9471 game_status_last == GAME_MODE_EDITOR);
9473 return (playfield_viewport_has_changed &&
9474 door_1_viewport_has_changed &&
9475 door_2_viewport_has_changed);
9478 boolean CheckFadeAll(void)
9480 return (CheckIfGlobalBorderHasChanged() ||
9481 CheckIfAllViewportsHaveChanged());
9484 void ChangeViewportPropertiesIfNeeded(void)
9486 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9487 FALSE : setup.small_game_graphics);
9488 int gfx_game_mode = game_status;
9489 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9491 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9492 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9493 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9494 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9495 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9496 int new_win_xsize = vp_window->width;
9497 int new_win_ysize = vp_window->height;
9498 int border_left = vp_playfield->border_left;
9499 int border_right = vp_playfield->border_right;
9500 int border_top = vp_playfield->border_top;
9501 int border_bottom = vp_playfield->border_bottom;
9502 int new_sx = vp_playfield->x + border_left;
9503 int new_sy = vp_playfield->y + border_top;
9504 int new_sxsize = vp_playfield->width - border_left - border_right;
9505 int new_sysize = vp_playfield->height - border_top - border_bottom;
9506 int new_real_sx = vp_playfield->x;
9507 int new_real_sy = vp_playfield->y;
9508 int new_full_sxsize = vp_playfield->width;
9509 int new_full_sysize = vp_playfield->height;
9510 int new_dx = vp_door_1->x;
9511 int new_dy = vp_door_1->y;
9512 int new_dxsize = vp_door_1->width;
9513 int new_dysize = vp_door_1->height;
9514 int new_vx = vp_door_2->x;
9515 int new_vy = vp_door_2->y;
9516 int new_vxsize = vp_door_2->width;
9517 int new_vysize = vp_door_2->height;
9518 int new_ex = vp_door_3->x;
9519 int new_ey = vp_door_3->y;
9520 int new_exsize = vp_door_3->width;
9521 int new_eysize = vp_door_3->height;
9522 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9523 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9524 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9525 int new_scr_fieldx = new_sxsize / tilesize;
9526 int new_scr_fieldy = new_sysize / tilesize;
9527 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9528 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9529 boolean init_gfx_buffers = FALSE;
9530 boolean init_video_buffer = FALSE;
9531 boolean init_gadgets_and_anims = FALSE;
9532 boolean init_em_graphics = FALSE;
9534 if (new_win_xsize != WIN_XSIZE ||
9535 new_win_ysize != WIN_YSIZE)
9537 WIN_XSIZE = new_win_xsize;
9538 WIN_YSIZE = new_win_ysize;
9540 init_video_buffer = TRUE;
9541 init_gfx_buffers = TRUE;
9542 init_gadgets_and_anims = TRUE;
9544 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9547 if (new_scr_fieldx != SCR_FIELDX ||
9548 new_scr_fieldy != SCR_FIELDY)
9550 // this always toggles between MAIN and GAME when using small tile size
9552 SCR_FIELDX = new_scr_fieldx;
9553 SCR_FIELDY = new_scr_fieldy;
9555 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9566 new_sxsize != SXSIZE ||
9567 new_sysize != SYSIZE ||
9568 new_dxsize != DXSIZE ||
9569 new_dysize != DYSIZE ||
9570 new_vxsize != VXSIZE ||
9571 new_vysize != VYSIZE ||
9572 new_exsize != EXSIZE ||
9573 new_eysize != EYSIZE ||
9574 new_real_sx != REAL_SX ||
9575 new_real_sy != REAL_SY ||
9576 new_full_sxsize != FULL_SXSIZE ||
9577 new_full_sysize != FULL_SYSIZE ||
9578 new_tilesize_var != TILESIZE_VAR
9581 // ------------------------------------------------------------------------
9582 // determine next fading area for changed viewport definitions
9583 // ------------------------------------------------------------------------
9585 // start with current playfield area (default fading area)
9588 FADE_SXSIZE = FULL_SXSIZE;
9589 FADE_SYSIZE = FULL_SYSIZE;
9591 // add new playfield area if position or size has changed
9592 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9593 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9595 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9596 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9599 // add current and new door 1 area if position or size has changed
9600 if (new_dx != DX || new_dy != DY ||
9601 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9603 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9604 DX, DY, DXSIZE, DYSIZE);
9605 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9606 new_dx, new_dy, new_dxsize, new_dysize);
9609 // add current and new door 2 area if position or size has changed
9610 if (new_vx != VX || new_vy != VY ||
9611 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9613 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9614 VX, VY, VXSIZE, VYSIZE);
9615 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9616 new_vx, new_vy, new_vxsize, new_vysize);
9619 // ------------------------------------------------------------------------
9620 // handle changed tile size
9621 // ------------------------------------------------------------------------
9623 if (new_tilesize_var != TILESIZE_VAR)
9625 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9627 // changing tile size invalidates scroll values of engine snapshots
9628 FreeEngineSnapshotSingle();
9630 // changing tile size requires update of graphic mapping for EM engine
9631 init_em_graphics = TRUE;
9642 SXSIZE = new_sxsize;
9643 SYSIZE = new_sysize;
9644 DXSIZE = new_dxsize;
9645 DYSIZE = new_dysize;
9646 VXSIZE = new_vxsize;
9647 VYSIZE = new_vysize;
9648 EXSIZE = new_exsize;
9649 EYSIZE = new_eysize;
9650 REAL_SX = new_real_sx;
9651 REAL_SY = new_real_sy;
9652 FULL_SXSIZE = new_full_sxsize;
9653 FULL_SYSIZE = new_full_sysize;
9654 TILESIZE_VAR = new_tilesize_var;
9656 init_gfx_buffers = TRUE;
9657 init_gadgets_and_anims = TRUE;
9659 // printf("::: viewports: init_gfx_buffers\n");
9660 // printf("::: viewports: init_gadgets_and_anims\n");
9663 if (init_gfx_buffers)
9665 // printf("::: init_gfx_buffers\n");
9667 SCR_FIELDX = new_scr_fieldx_buffers;
9668 SCR_FIELDY = new_scr_fieldy_buffers;
9672 SCR_FIELDX = new_scr_fieldx;
9673 SCR_FIELDY = new_scr_fieldy;
9675 SetDrawDeactivationMask(REDRAW_NONE);
9676 SetDrawBackgroundMask(REDRAW_FIELD);
9679 if (init_video_buffer)
9681 // printf("::: init_video_buffer\n");
9683 FreeAllImageTextures(); // needs old renderer to free the textures
9685 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9686 InitImageTextures();
9689 if (init_gadgets_and_anims)
9691 // printf("::: init_gadgets_and_anims\n");
9694 InitGlobalAnimations();
9697 if (init_em_graphics)
9699 InitGraphicInfo_EM();