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) &&
1068 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1071 SetScreenStates_BeforeFadingOut();
1073 SetTileCursorActive(FALSE);
1074 SetOverlayActive(FALSE);
1077 DrawMaskedBorder(REDRAW_ALL);
1080 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1081 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1083 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1085 SetScreenStates_AfterFadingOut();
1088 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1090 static struct TitleFadingInfo fading_leave_stored;
1093 fading_leave_stored = fading_leave;
1095 fading = fading_leave_stored;
1098 void FadeSetEnterMenu(void)
1100 fading = menu.enter_menu;
1102 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1105 void FadeSetLeaveMenu(void)
1107 fading = menu.leave_menu;
1109 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1112 void FadeSetEnterScreen(void)
1114 fading = menu.enter_screen[game_status];
1116 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1119 void FadeSetNextScreen(void)
1121 fading = menu.next_screen[game_status];
1123 // (do not overwrite fade mode set by FadeSetEnterScreen)
1124 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1127 void FadeSetLeaveScreen(void)
1129 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1132 void FadeSetFromType(int type)
1134 if (type & TYPE_ENTER_SCREEN)
1135 FadeSetEnterScreen();
1136 else if (type & TYPE_ENTER)
1138 else if (type & TYPE_LEAVE)
1142 void FadeSetDisabled(void)
1144 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1146 fading = fading_none;
1149 void FadeSkipNextFadeIn(void)
1151 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1154 void FadeSkipNextFadeOut(void)
1156 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1159 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1161 if (graphic == IMG_UNDEFINED)
1164 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1166 return (graphic_info[graphic].bitmap != NULL || redefined ?
1167 graphic_info[graphic].bitmap :
1168 graphic_info[default_graphic].bitmap);
1171 static Bitmap *getBackgroundBitmap(int graphic)
1173 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1176 static Bitmap *getGlobalBorderBitmap(int graphic)
1178 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1181 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1184 (status == GAME_MODE_MAIN ||
1185 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1186 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1187 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1188 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1191 return getGlobalBorderBitmap(graphic);
1194 void SetWindowBackgroundImageIfDefined(int graphic)
1196 if (graphic_info[graphic].bitmap)
1197 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1200 void SetMainBackgroundImageIfDefined(int graphic)
1202 if (graphic_info[graphic].bitmap)
1203 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1206 void SetDoorBackgroundImageIfDefined(int graphic)
1208 if (graphic_info[graphic].bitmap)
1209 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1212 void SetWindowBackgroundImage(int graphic)
1214 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1217 void SetMainBackgroundImage(int graphic)
1219 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1222 void SetDoorBackgroundImage(int graphic)
1224 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1227 void SetPanelBackground(void)
1229 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1231 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1232 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1234 SetDoorBackgroundBitmap(bitmap_db_panel);
1237 void DrawBackground(int x, int y, int width, int height)
1239 // "drawto" might still point to playfield buffer here (hall of fame)
1240 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1242 if (IN_GFX_FIELD_FULL(x, y))
1243 redraw_mask |= REDRAW_FIELD;
1244 else if (IN_GFX_DOOR_1(x, y))
1245 redraw_mask |= REDRAW_DOOR_1;
1246 else if (IN_GFX_DOOR_2(x, y))
1247 redraw_mask |= REDRAW_DOOR_2;
1248 else if (IN_GFX_DOOR_3(x, y))
1249 redraw_mask |= REDRAW_DOOR_3;
1252 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1254 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1256 if (font->bitmap == NULL)
1259 DrawBackground(x, y, width, height);
1262 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1264 struct GraphicInfo *g = &graphic_info[graphic];
1266 if (g->bitmap == NULL)
1269 DrawBackground(x, y, width, height);
1272 static int game_status_last = -1;
1273 static Bitmap *global_border_bitmap_last = NULL;
1274 static Bitmap *global_border_bitmap = NULL;
1275 static int real_sx_last = -1, real_sy_last = -1;
1276 static int full_sxsize_last = -1, full_sysize_last = -1;
1277 static int dx_last = -1, dy_last = -1;
1278 static int dxsize_last = -1, dysize_last = -1;
1279 static int vx_last = -1, vy_last = -1;
1280 static int vxsize_last = -1, vysize_last = -1;
1281 static int ex_last = -1, ey_last = -1;
1282 static int exsize_last = -1, eysize_last = -1;
1284 boolean CheckIfGlobalBorderHasChanged(void)
1286 // if game status has not changed, global border has not changed either
1287 if (game_status == game_status_last)
1290 // determine and store new global border bitmap for current game status
1291 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1293 return (global_border_bitmap_last != global_border_bitmap);
1296 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1298 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1299 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1301 // if game status has not changed, nothing has to be redrawn
1302 if (game_status == game_status_last)
1305 // redraw if last screen was title screen
1306 if (game_status_last == GAME_MODE_TITLE)
1309 // redraw if global screen border has changed
1310 if (CheckIfGlobalBorderHasChanged())
1313 // redraw if position or size of playfield area has changed
1314 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1315 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1318 // redraw if position or size of door area has changed
1319 if (dx_last != DX || dy_last != DY ||
1320 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1323 // redraw if position or size of tape area has changed
1324 if (vx_last != VX || vy_last != VY ||
1325 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1328 // redraw if position or size of editor area has changed
1329 if (ex_last != EX || ey_last != EY ||
1330 exsize_last != EXSIZE || eysize_last != EYSIZE)
1337 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1340 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1342 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1345 void RedrawGlobalBorder(void)
1347 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1349 RedrawGlobalBorderFromBitmap(bitmap);
1351 redraw_mask = REDRAW_ALL;
1354 static void RedrawGlobalBorderIfNeeded(void)
1356 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1357 if (game_status == game_status_last)
1361 // copy current draw buffer to later copy back areas that have not changed
1362 if (game_status_last != GAME_MODE_TITLE)
1363 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1365 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1366 if (CheckIfGlobalBorderRedrawIsNeeded())
1369 // redraw global screen border (or clear, if defined to be empty)
1370 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1372 if (game_status == GAME_MODE_EDITOR)
1373 DrawSpecialEditorDoor();
1375 // copy previous playfield and door areas, if they are defined on both
1376 // previous and current screen and if they still have the same size
1378 if (real_sx_last != -1 && real_sy_last != -1 &&
1379 REAL_SX != -1 && REAL_SY != -1 &&
1380 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1381 BlitBitmap(bitmap_db_store_1, backbuffer,
1382 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1385 if (dx_last != -1 && dy_last != -1 &&
1386 DX != -1 && DY != -1 &&
1387 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1388 BlitBitmap(bitmap_db_store_1, backbuffer,
1389 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1391 if (game_status != GAME_MODE_EDITOR)
1393 if (vx_last != -1 && vy_last != -1 &&
1394 VX != -1 && VY != -1 &&
1395 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1396 BlitBitmap(bitmap_db_store_1, backbuffer,
1397 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1401 if (ex_last != -1 && ey_last != -1 &&
1402 EX != -1 && EY != -1 &&
1403 exsize_last == EXSIZE && eysize_last == EYSIZE)
1404 BlitBitmap(bitmap_db_store_1, backbuffer,
1405 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1408 redraw_mask = REDRAW_ALL;
1411 game_status_last = game_status;
1413 global_border_bitmap_last = global_border_bitmap;
1415 real_sx_last = REAL_SX;
1416 real_sy_last = REAL_SY;
1417 full_sxsize_last = FULL_SXSIZE;
1418 full_sysize_last = FULL_SYSIZE;
1421 dxsize_last = DXSIZE;
1422 dysize_last = DYSIZE;
1425 vxsize_last = VXSIZE;
1426 vysize_last = VYSIZE;
1429 exsize_last = EXSIZE;
1430 eysize_last = EYSIZE;
1433 void ClearField(void)
1435 RedrawGlobalBorderIfNeeded();
1437 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1438 // (when entering hall of fame after playing)
1439 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1441 // !!! maybe this should be done before clearing the background !!!
1442 if (game_status == GAME_MODE_PLAYING)
1444 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1445 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1449 SetDrawtoField(DRAW_TO_BACKBUFFER);
1453 void MarkTileDirty(int x, int y)
1455 redraw_mask |= REDRAW_FIELD;
1458 void SetBorderElement(void)
1462 BorderElement = EL_EMPTY;
1464 // the MM game engine does not use a visible border element
1465 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1468 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1470 for (x = 0; x < lev_fieldx; x++)
1472 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1473 BorderElement = EL_STEELWALL;
1475 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1481 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1482 int max_array_fieldx, int max_array_fieldy,
1483 short field[max_array_fieldx][max_array_fieldy],
1484 int max_fieldx, int max_fieldy)
1488 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1489 static int safety = 0;
1491 // check if starting field still has the desired content
1492 if (field[from_x][from_y] == fill_element)
1497 if (safety > max_fieldx * max_fieldy)
1498 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1500 old_element = field[from_x][from_y];
1501 field[from_x][from_y] = fill_element;
1503 for (i = 0; i < 4; i++)
1505 x = from_x + check[i][0];
1506 y = from_y + check[i][1];
1508 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1509 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1510 field, max_fieldx, max_fieldy);
1516 void FloodFillLevel(int from_x, int from_y, int fill_element,
1517 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1518 int max_fieldx, int max_fieldy)
1520 FloodFillLevelExt(from_x, from_y, fill_element,
1521 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1522 max_fieldx, max_fieldy);
1525 void SetRandomAnimationValue(int x, int y)
1527 gfx.anim_random_frame = GfxRandom[x][y];
1530 int getGraphicAnimationFrame(int graphic, int sync_frame)
1532 // animation synchronized with global frame counter, not move position
1533 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1534 sync_frame = FrameCounter;
1536 return getAnimationFrame(graphic_info[graphic].anim_frames,
1537 graphic_info[graphic].anim_delay,
1538 graphic_info[graphic].anim_mode,
1539 graphic_info[graphic].anim_start_frame,
1543 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1545 struct GraphicInfo *g = &graphic_info[graphic];
1546 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1548 if (tilesize == gfx.standard_tile_size)
1549 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1550 else if (tilesize == game.tile_size)
1551 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1553 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1556 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1557 boolean get_backside)
1559 struct GraphicInfo *g = &graphic_info[graphic];
1560 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1561 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1563 if (g->offset_y == 0) // frames are ordered horizontally
1565 int max_width = g->anim_frames_per_line * g->width;
1566 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1568 *x = pos % max_width;
1569 *y = src_y % g->height + pos / max_width * g->height;
1571 else if (g->offset_x == 0) // frames are ordered vertically
1573 int max_height = g->anim_frames_per_line * g->height;
1574 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1576 *x = src_x % g->width + pos / max_height * g->width;
1577 *y = pos % max_height;
1579 else // frames are ordered diagonally
1581 *x = src_x + frame * g->offset_x;
1582 *y = src_y + frame * g->offset_y;
1586 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1587 Bitmap **bitmap, int *x, int *y,
1588 boolean get_backside)
1590 struct GraphicInfo *g = &graphic_info[graphic];
1592 // if no graphics defined at all, use fallback graphics
1593 if (g->bitmaps == NULL)
1594 *g = graphic_info[IMG_CHAR_EXCLAM];
1596 // if no in-game graphics defined, always use standard graphic size
1597 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1598 tilesize = TILESIZE;
1600 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1601 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1603 *x = *x * tilesize / g->tile_size;
1604 *y = *y * tilesize / g->tile_size;
1607 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1608 Bitmap **bitmap, int *x, int *y)
1610 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1613 void getFixedGraphicSource(int graphic, int frame,
1614 Bitmap **bitmap, int *x, int *y)
1616 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1619 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1621 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1624 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1625 int *x, int *y, boolean get_backside)
1627 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1631 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1633 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1636 void DrawGraphic(int x, int y, int graphic, int frame)
1639 if (!IN_SCR_FIELD(x, y))
1641 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1642 printf("DrawGraphic(): This should never happen!\n");
1647 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1650 MarkTileDirty(x, y);
1653 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1656 if (!IN_SCR_FIELD(x, y))
1658 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1659 printf("DrawGraphic(): This should never happen!\n");
1664 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1666 MarkTileDirty(x, y);
1669 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1675 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1677 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1680 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1686 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1687 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1690 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1693 if (!IN_SCR_FIELD(x, y))
1695 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1696 printf("DrawGraphicThruMask(): This should never happen!\n");
1701 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1704 MarkTileDirty(x, y);
1707 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1710 if (!IN_SCR_FIELD(x, y))
1712 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1713 printf("DrawGraphicThruMask(): This should never happen!\n");
1718 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1720 MarkTileDirty(x, y);
1723 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1729 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1731 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1735 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1736 int graphic, int frame)
1741 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1743 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1747 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1749 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1751 MarkTileDirty(x / tilesize, y / tilesize);
1754 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1757 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1758 graphic, frame, tilesize);
1759 MarkTileDirty(x / tilesize, y / tilesize);
1762 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1768 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1769 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1772 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1773 int frame, int tilesize)
1778 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1779 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1782 void DrawMiniGraphic(int x, int y, int graphic)
1784 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1785 MarkTileDirty(x / 2, y / 2);
1788 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1793 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1794 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1797 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1798 int graphic, int frame,
1799 int cut_mode, int mask_mode)
1804 int width = TILEX, height = TILEY;
1807 if (dx || dy) // shifted graphic
1809 if (x < BX1) // object enters playfield from the left
1816 else if (x > BX2) // object enters playfield from the right
1822 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1828 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1830 else if (dx) // general horizontal movement
1831 MarkTileDirty(x + SIGN(dx), y);
1833 if (y < BY1) // object enters playfield from the top
1835 if (cut_mode == CUT_BELOW) // object completely above top border
1843 else if (y > BY2) // object enters playfield from the bottom
1849 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1855 else if (dy > 0 && cut_mode == CUT_ABOVE)
1857 if (y == BY2) // object completely above bottom border
1863 MarkTileDirty(x, y + 1);
1864 } // object leaves playfield to the bottom
1865 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1867 else if (dy) // general vertical movement
1868 MarkTileDirty(x, y + SIGN(dy));
1872 if (!IN_SCR_FIELD(x, y))
1874 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1875 printf("DrawGraphicShifted(): This should never happen!\n");
1880 width = width * TILESIZE_VAR / TILESIZE;
1881 height = height * TILESIZE_VAR / TILESIZE;
1882 cx = cx * TILESIZE_VAR / TILESIZE;
1883 cy = cy * TILESIZE_VAR / TILESIZE;
1884 dx = dx * TILESIZE_VAR / TILESIZE;
1885 dy = dy * TILESIZE_VAR / TILESIZE;
1887 if (width > 0 && height > 0)
1889 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1894 dst_x = FX + x * TILEX_VAR + dx;
1895 dst_y = FY + y * TILEY_VAR + dy;
1897 if (mask_mode == USE_MASKING)
1898 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1901 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1904 MarkTileDirty(x, y);
1908 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1909 int graphic, int frame,
1910 int cut_mode, int mask_mode)
1915 int width = TILEX_VAR, height = TILEY_VAR;
1918 int x2 = x + SIGN(dx);
1919 int y2 = y + SIGN(dy);
1921 // movement with two-tile animations must be sync'ed with movement position,
1922 // not with current GfxFrame (which can be higher when using slow movement)
1923 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1924 int anim_frames = graphic_info[graphic].anim_frames;
1926 // (we also need anim_delay here for movement animations with less frames)
1927 int anim_delay = graphic_info[graphic].anim_delay;
1928 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1930 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1931 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1933 // re-calculate animation frame for two-tile movement animation
1934 frame = getGraphicAnimationFrame(graphic, sync_frame);
1936 // check if movement start graphic inside screen area and should be drawn
1937 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1939 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1941 dst_x = FX + x1 * TILEX_VAR;
1942 dst_y = FY + y1 * TILEY_VAR;
1944 if (mask_mode == USE_MASKING)
1945 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1948 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 MarkTileDirty(x1, y1);
1954 // check if movement end graphic inside screen area and should be drawn
1955 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1957 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1959 dst_x = FX + x2 * TILEX_VAR;
1960 dst_y = FY + y2 * TILEY_VAR;
1962 if (mask_mode == USE_MASKING)
1963 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1966 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1969 MarkTileDirty(x2, y2);
1973 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1974 int graphic, int frame,
1975 int cut_mode, int mask_mode)
1979 DrawGraphic(x, y, graphic, frame);
1984 if (graphic_info[graphic].double_movement) // EM style movement images
1985 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1987 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1990 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1991 int graphic, int frame, int cut_mode)
1993 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1996 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1997 int cut_mode, int mask_mode)
1999 int lx = LEVELX(x), ly = LEVELY(y);
2003 if (IN_LEV_FIELD(lx, ly))
2005 SetRandomAnimationValue(lx, ly);
2007 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2008 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2010 // do not use double (EM style) movement graphic when not moving
2011 if (graphic_info[graphic].double_movement && !dx && !dy)
2013 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2014 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2017 else // border element
2019 graphic = el2img(element);
2020 frame = getGraphicAnimationFrame(graphic, -1);
2023 if (element == EL_EXPANDABLE_WALL)
2025 boolean left_stopped = FALSE, right_stopped = FALSE;
2027 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2028 left_stopped = TRUE;
2029 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2030 right_stopped = TRUE;
2032 if (left_stopped && right_stopped)
2034 else if (left_stopped)
2036 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2037 frame = graphic_info[graphic].anim_frames - 1;
2039 else if (right_stopped)
2041 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2042 frame = graphic_info[graphic].anim_frames - 1;
2047 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2048 else if (mask_mode == USE_MASKING)
2049 DrawGraphicThruMask(x, y, graphic, frame);
2051 DrawGraphic(x, y, graphic, frame);
2054 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2055 int cut_mode, int mask_mode)
2057 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2058 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2059 cut_mode, mask_mode);
2062 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2065 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2068 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2071 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2074 void DrawLevelElementThruMask(int x, int y, int element)
2076 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2079 void DrawLevelFieldThruMask(int x, int y)
2081 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2084 // !!! implementation of quicksand is totally broken !!!
2085 #define IS_CRUMBLED_TILE(x, y, e) \
2086 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2087 !IS_MOVING(x, y) || \
2088 (e) == EL_QUICKSAND_EMPTYING || \
2089 (e) == EL_QUICKSAND_FAST_EMPTYING))
2091 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2096 int width, height, cx, cy;
2097 int sx = SCREENX(x), sy = SCREENY(y);
2098 int crumbled_border_size = graphic_info[graphic].border_size;
2099 int crumbled_tile_size = graphic_info[graphic].tile_size;
2100 int crumbled_border_size_var =
2101 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2104 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2106 for (i = 1; i < 4; i++)
2108 int dxx = (i & 1 ? dx : 0);
2109 int dyy = (i & 2 ? dy : 0);
2112 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2115 // check if neighbour field is of same crumble type
2116 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2117 graphic_info[graphic].class ==
2118 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2120 // return if check prevents inner corner
2121 if (same == (dxx == dx && dyy == dy))
2125 // if we reach this point, we have an inner corner
2127 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2129 width = crumbled_border_size_var;
2130 height = crumbled_border_size_var;
2131 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2132 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2134 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2135 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2138 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2143 int width, height, bx, by, cx, cy;
2144 int sx = SCREENX(x), sy = SCREENY(y);
2145 int crumbled_border_size = graphic_info[graphic].border_size;
2146 int crumbled_tile_size = graphic_info[graphic].tile_size;
2147 int crumbled_border_size_var =
2148 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2149 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2152 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2154 // draw simple, sloppy, non-corner-accurate crumbled border
2156 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2157 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2158 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2159 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2161 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2162 FX + sx * TILEX_VAR + cx,
2163 FY + sy * TILEY_VAR + cy);
2165 // (remaining middle border part must be at least as big as corner part)
2166 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2167 crumbled_border_size_var >= TILESIZE_VAR / 3)
2170 // correct corners of crumbled border, if needed
2172 for (i = -1; i <= 1; i += 2)
2174 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2175 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2176 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2179 // check if neighbour field is of same crumble type
2180 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2181 graphic_info[graphic].class ==
2182 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2184 // no crumbled corner, but continued crumbled border
2186 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2187 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2188 int b1 = (i == 1 ? crumbled_border_size_var :
2189 TILESIZE_VAR - 2 * crumbled_border_size_var);
2191 width = crumbled_border_size_var;
2192 height = crumbled_border_size_var;
2194 if (dir == 1 || dir == 2)
2209 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2211 FX + sx * TILEX_VAR + cx,
2212 FY + sy * TILEY_VAR + cy);
2217 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2219 int sx = SCREENX(x), sy = SCREENY(y);
2222 static int xy[4][2] =
2230 if (!IN_LEV_FIELD(x, y))
2233 element = TILE_GFX_ELEMENT(x, y);
2235 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2237 if (!IN_SCR_FIELD(sx, sy))
2240 // crumble field borders towards direct neighbour fields
2241 for (i = 0; i < 4; i++)
2243 int xx = x + xy[i][0];
2244 int yy = y + xy[i][1];
2246 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2249 // check if neighbour field is of same crumble type
2250 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2251 graphic_info[graphic].class ==
2252 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2255 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2258 // crumble inner field corners towards corner neighbour fields
2259 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2260 graphic_info[graphic].anim_frames == 2)
2262 for (i = 0; i < 4; i++)
2264 int dx = (i & 1 ? +1 : -1);
2265 int dy = (i & 2 ? +1 : -1);
2267 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2271 MarkTileDirty(sx, sy);
2273 else // center field is not crumbled -- crumble neighbour fields
2275 // crumble field borders of direct neighbour fields
2276 for (i = 0; i < 4; i++)
2278 int xx = x + xy[i][0];
2279 int yy = y + xy[i][1];
2280 int sxx = sx + xy[i][0];
2281 int syy = sy + xy[i][1];
2283 if (!IN_LEV_FIELD(xx, yy) ||
2284 !IN_SCR_FIELD(sxx, syy))
2287 // do not crumble fields that are being digged or snapped
2288 if (Feld[xx][yy] == EL_EMPTY ||
2289 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2292 element = TILE_GFX_ELEMENT(xx, yy);
2294 if (!IS_CRUMBLED_TILE(xx, yy, element))
2297 graphic = el_act2crm(element, ACTION_DEFAULT);
2299 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2301 MarkTileDirty(sxx, syy);
2304 // crumble inner field corners of corner neighbour fields
2305 for (i = 0; i < 4; i++)
2307 int dx = (i & 1 ? +1 : -1);
2308 int dy = (i & 2 ? +1 : -1);
2314 if (!IN_LEV_FIELD(xx, yy) ||
2315 !IN_SCR_FIELD(sxx, syy))
2318 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2321 element = TILE_GFX_ELEMENT(xx, yy);
2323 if (!IS_CRUMBLED_TILE(xx, yy, element))
2326 graphic = el_act2crm(element, ACTION_DEFAULT);
2328 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2329 graphic_info[graphic].anim_frames == 2)
2330 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2332 MarkTileDirty(sxx, syy);
2337 void DrawLevelFieldCrumbled(int x, int y)
2341 if (!IN_LEV_FIELD(x, y))
2344 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2345 GfxElement[x][y] != EL_UNDEFINED &&
2346 GFX_CRUMBLED(GfxElement[x][y]))
2348 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2353 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2355 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2358 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2361 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2362 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2363 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2364 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2365 int sx = SCREENX(x), sy = SCREENY(y);
2367 DrawGraphic(sx, sy, graphic1, frame1);
2368 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2371 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2373 int sx = SCREENX(x), sy = SCREENY(y);
2374 static int xy[4][2] =
2383 // crumble direct neighbour fields (required for field borders)
2384 for (i = 0; i < 4; i++)
2386 int xx = x + xy[i][0];
2387 int yy = y + xy[i][1];
2388 int sxx = sx + xy[i][0];
2389 int syy = sy + xy[i][1];
2391 if (!IN_LEV_FIELD(xx, yy) ||
2392 !IN_SCR_FIELD(sxx, syy) ||
2393 !GFX_CRUMBLED(Feld[xx][yy]) ||
2397 DrawLevelField(xx, yy);
2400 // crumble corner neighbour fields (required for inner field corners)
2401 for (i = 0; i < 4; i++)
2403 int dx = (i & 1 ? +1 : -1);
2404 int dy = (i & 2 ? +1 : -1);
2410 if (!IN_LEV_FIELD(xx, yy) ||
2411 !IN_SCR_FIELD(sxx, syy) ||
2412 !GFX_CRUMBLED(Feld[xx][yy]) ||
2416 int element = TILE_GFX_ELEMENT(xx, yy);
2417 int graphic = el_act2crm(element, ACTION_DEFAULT);
2419 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2420 graphic_info[graphic].anim_frames == 2)
2421 DrawLevelField(xx, yy);
2425 static int getBorderElement(int x, int y)
2429 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2430 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2431 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2432 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2433 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2434 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2435 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2437 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2438 int steel_position = (x == -1 && y == -1 ? 0 :
2439 x == lev_fieldx && y == -1 ? 1 :
2440 x == -1 && y == lev_fieldy ? 2 :
2441 x == lev_fieldx && y == lev_fieldy ? 3 :
2442 x == -1 || x == lev_fieldx ? 4 :
2443 y == -1 || y == lev_fieldy ? 5 : 6);
2445 return border[steel_position][steel_type];
2448 void DrawScreenElement(int x, int y, int element)
2450 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2451 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2454 void DrawLevelElement(int x, int y, int element)
2456 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2457 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2460 void DrawScreenField(int x, int y)
2462 int lx = LEVELX(x), ly = LEVELY(y);
2463 int element, content;
2465 if (!IN_LEV_FIELD(lx, ly))
2467 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2470 element = getBorderElement(lx, ly);
2472 DrawScreenElement(x, y, element);
2477 element = Feld[lx][ly];
2478 content = Store[lx][ly];
2480 if (IS_MOVING(lx, ly))
2482 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2483 boolean cut_mode = NO_CUTTING;
2485 if (element == EL_QUICKSAND_EMPTYING ||
2486 element == EL_QUICKSAND_FAST_EMPTYING ||
2487 element == EL_MAGIC_WALL_EMPTYING ||
2488 element == EL_BD_MAGIC_WALL_EMPTYING ||
2489 element == EL_DC_MAGIC_WALL_EMPTYING ||
2490 element == EL_AMOEBA_DROPPING)
2491 cut_mode = CUT_ABOVE;
2492 else if (element == EL_QUICKSAND_FILLING ||
2493 element == EL_QUICKSAND_FAST_FILLING ||
2494 element == EL_MAGIC_WALL_FILLING ||
2495 element == EL_BD_MAGIC_WALL_FILLING ||
2496 element == EL_DC_MAGIC_WALL_FILLING)
2497 cut_mode = CUT_BELOW;
2499 if (cut_mode == CUT_ABOVE)
2500 DrawScreenElement(x, y, element);
2502 DrawScreenElement(x, y, EL_EMPTY);
2505 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2506 else if (cut_mode == NO_CUTTING)
2507 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2510 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2512 if (cut_mode == CUT_BELOW &&
2513 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2514 DrawLevelElement(lx, ly + 1, element);
2517 if (content == EL_ACID)
2519 int dir = MovDir[lx][ly];
2520 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2521 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2523 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2525 // prevent target field from being drawn again (but without masking)
2526 // (this would happen if target field is scanned after moving element)
2527 Stop[newlx][newly] = TRUE;
2530 else if (IS_BLOCKED(lx, ly))
2535 boolean cut_mode = NO_CUTTING;
2536 int element_old, content_old;
2538 Blocked2Moving(lx, ly, &oldx, &oldy);
2541 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2542 MovDir[oldx][oldy] == MV_RIGHT);
2544 element_old = Feld[oldx][oldy];
2545 content_old = Store[oldx][oldy];
2547 if (element_old == EL_QUICKSAND_EMPTYING ||
2548 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2549 element_old == EL_MAGIC_WALL_EMPTYING ||
2550 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2551 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2552 element_old == EL_AMOEBA_DROPPING)
2553 cut_mode = CUT_ABOVE;
2555 DrawScreenElement(x, y, EL_EMPTY);
2558 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2560 else if (cut_mode == NO_CUTTING)
2561 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2564 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2567 else if (IS_DRAWABLE(element))
2568 DrawScreenElement(x, y, element);
2570 DrawScreenElement(x, y, EL_EMPTY);
2573 void DrawLevelField(int x, int y)
2575 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2576 DrawScreenField(SCREENX(x), SCREENY(y));
2577 else if (IS_MOVING(x, y))
2581 Moving2Blocked(x, y, &newx, &newy);
2582 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2583 DrawScreenField(SCREENX(newx), SCREENY(newy));
2585 else if (IS_BLOCKED(x, y))
2589 Blocked2Moving(x, y, &oldx, &oldy);
2590 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2591 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2595 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2596 int (*el2img_function)(int), boolean masked,
2597 int element_bits_draw)
2599 int element_base = map_mm_wall_element(element);
2600 int element_bits = (IS_DF_WALL(element) ?
2601 element - EL_DF_WALL_START :
2602 IS_MM_WALL(element) ?
2603 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2604 int graphic = el2img_function(element_base);
2605 int tilesize_draw = tilesize / 2;
2610 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2612 for (i = 0; i < 4; i++)
2614 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2615 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2617 if (!(element_bits_draw & (1 << i)))
2620 if (element_bits & (1 << i))
2623 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2624 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2626 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2627 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2632 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2633 tilesize_draw, tilesize_draw);
2638 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2639 boolean masked, int element_bits_draw)
2641 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2642 element, tilesize, el2edimg, masked, element_bits_draw);
2645 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2646 int (*el2img_function)(int))
2648 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2652 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2655 if (IS_MM_WALL(element))
2657 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2658 element, tilesize, el2edimg, masked, 0x000f);
2662 int graphic = el2edimg(element);
2665 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2667 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2671 void DrawSizedElement(int x, int y, int element, int tilesize)
2673 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2676 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2678 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2681 void DrawMiniElement(int x, int y, int element)
2685 graphic = el2edimg(element);
2686 DrawMiniGraphic(x, y, graphic);
2689 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2692 int x = sx + scroll_x, y = sy + scroll_y;
2694 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2695 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2696 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2697 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2699 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2702 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2704 int x = sx + scroll_x, y = sy + scroll_y;
2706 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2707 DrawMiniElement(sx, sy, EL_EMPTY);
2708 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2709 DrawMiniElement(sx, sy, Feld[x][y]);
2711 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2714 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2715 int x, int y, int xsize, int ysize,
2716 int tile_width, int tile_height)
2720 int dst_x = startx + x * tile_width;
2721 int dst_y = starty + y * tile_height;
2722 int width = graphic_info[graphic].width;
2723 int height = graphic_info[graphic].height;
2724 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2725 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2726 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2727 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2728 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2729 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2730 boolean draw_masked = graphic_info[graphic].draw_masked;
2732 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2734 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2736 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2740 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2741 inner_sx + (x - 1) * tile_width % inner_width);
2742 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2743 inner_sy + (y - 1) * tile_height % inner_height);
2746 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2749 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2753 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2754 int x, int y, int xsize, int ysize,
2757 int font_width = getFontWidth(font_nr);
2758 int font_height = getFontHeight(font_nr);
2760 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2761 font_width, font_height);
2764 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2766 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2767 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2768 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2769 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2770 boolean no_delay = (tape.warp_forward);
2771 unsigned int anim_delay = 0;
2772 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2773 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2774 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2775 int font_width = getFontWidth(font_nr);
2776 int font_height = getFontHeight(font_nr);
2777 int max_xsize = level.envelope[envelope_nr].xsize;
2778 int max_ysize = level.envelope[envelope_nr].ysize;
2779 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2780 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2781 int xend = max_xsize;
2782 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2783 int xstep = (xstart < xend ? 1 : 0);
2784 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2786 int end = MAX(xend - xstart, yend - ystart);
2789 for (i = start; i <= end; i++)
2791 int last_frame = end; // last frame of this "for" loop
2792 int x = xstart + i * xstep;
2793 int y = ystart + i * ystep;
2794 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2795 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2796 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2797 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2800 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2802 BlitScreenToBitmap(backbuffer);
2804 SetDrawtoField(DRAW_TO_BACKBUFFER);
2806 for (yy = 0; yy < ysize; yy++)
2807 for (xx = 0; xx < xsize; xx++)
2808 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2810 DrawTextBuffer(sx + font_width, sy + font_height,
2811 level.envelope[envelope_nr].text, font_nr, max_xsize,
2812 xsize - 2, ysize - 2, 0, mask_mode,
2813 level.envelope[envelope_nr].autowrap,
2814 level.envelope[envelope_nr].centered, FALSE);
2816 redraw_mask |= REDRAW_FIELD;
2819 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2822 ClearAutoRepeatKeyEvents();
2825 void ShowEnvelope(int envelope_nr)
2827 int element = EL_ENVELOPE_1 + envelope_nr;
2828 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2829 int sound_opening = element_info[element].sound[ACTION_OPENING];
2830 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2831 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2832 boolean no_delay = (tape.warp_forward);
2833 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2834 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2835 int anim_mode = graphic_info[graphic].anim_mode;
2836 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2837 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2839 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2841 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2843 if (anim_mode == ANIM_DEFAULT)
2844 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2846 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2849 Delay_WithScreenUpdates(wait_delay_value);
2851 WaitForEventToContinue();
2853 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2855 if (anim_mode != ANIM_NONE)
2856 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2858 if (anim_mode == ANIM_DEFAULT)
2859 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2861 game.envelope_active = FALSE;
2863 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2865 redraw_mask |= REDRAW_FIELD;
2869 static void setRequestBasePosition(int *x, int *y)
2871 int sx_base, sy_base;
2873 if (request.x != -1)
2874 sx_base = request.x;
2875 else if (request.align == ALIGN_LEFT)
2877 else if (request.align == ALIGN_RIGHT)
2878 sx_base = SX + SXSIZE;
2880 sx_base = SX + SXSIZE / 2;
2882 if (request.y != -1)
2883 sy_base = request.y;
2884 else if (request.valign == VALIGN_TOP)
2886 else if (request.valign == VALIGN_BOTTOM)
2887 sy_base = SY + SYSIZE;
2889 sy_base = SY + SYSIZE / 2;
2895 static void setRequestPositionExt(int *x, int *y, int width, int height,
2896 boolean add_border_size)
2898 int border_size = request.border_size;
2899 int sx_base, sy_base;
2902 setRequestBasePosition(&sx_base, &sy_base);
2904 if (request.align == ALIGN_LEFT)
2906 else if (request.align == ALIGN_RIGHT)
2907 sx = sx_base - width;
2909 sx = sx_base - width / 2;
2911 if (request.valign == VALIGN_TOP)
2913 else if (request.valign == VALIGN_BOTTOM)
2914 sy = sy_base - height;
2916 sy = sy_base - height / 2;
2918 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2919 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2921 if (add_border_size)
2931 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2933 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2936 static void DrawEnvelopeRequest(char *text)
2938 char *text_final = text;
2939 char *text_door_style = NULL;
2940 int graphic = IMG_BACKGROUND_REQUEST;
2941 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2942 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2943 int font_nr = FONT_REQUEST;
2944 int font_width = getFontWidth(font_nr);
2945 int font_height = getFontHeight(font_nr);
2946 int border_size = request.border_size;
2947 int line_spacing = request.line_spacing;
2948 int line_height = font_height + line_spacing;
2949 int max_text_width = request.width - 2 * border_size;
2950 int max_text_height = request.height - 2 * border_size;
2951 int line_length = max_text_width / font_width;
2952 int max_lines = max_text_height / line_height;
2953 int text_width = line_length * font_width;
2954 int width = request.width;
2955 int height = request.height;
2956 int tile_size = MAX(request.step_offset, 1);
2957 int x_steps = width / tile_size;
2958 int y_steps = height / tile_size;
2959 int sx_offset = border_size;
2960 int sy_offset = border_size;
2964 if (request.centered)
2965 sx_offset = (request.width - text_width) / 2;
2967 if (request.wrap_single_words && !request.autowrap)
2969 char *src_text_ptr, *dst_text_ptr;
2971 text_door_style = checked_malloc(2 * strlen(text) + 1);
2973 src_text_ptr = text;
2974 dst_text_ptr = text_door_style;
2976 while (*src_text_ptr)
2978 if (*src_text_ptr == ' ' ||
2979 *src_text_ptr == '?' ||
2980 *src_text_ptr == '!')
2981 *dst_text_ptr++ = '\n';
2983 if (*src_text_ptr != ' ')
2984 *dst_text_ptr++ = *src_text_ptr;
2989 *dst_text_ptr = '\0';
2991 text_final = text_door_style;
2994 setRequestPosition(&sx, &sy, FALSE);
2996 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2998 for (y = 0; y < y_steps; y++)
2999 for (x = 0; x < x_steps; x++)
3000 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3001 x, y, x_steps, y_steps,
3002 tile_size, tile_size);
3004 // force DOOR font inside door area
3005 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3007 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3008 line_length, -1, max_lines, line_spacing, mask_mode,
3009 request.autowrap, request.centered, FALSE);
3013 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3014 RedrawGadget(tool_gadget[i]);
3016 // store readily prepared envelope request for later use when animating
3017 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3019 if (text_door_style)
3020 free(text_door_style);
3023 static void AnimateEnvelopeRequest(int anim_mode, int action)
3025 int graphic = IMG_BACKGROUND_REQUEST;
3026 boolean draw_masked = graphic_info[graphic].draw_masked;
3027 int delay_value_normal = request.step_delay;
3028 int delay_value_fast = delay_value_normal / 2;
3029 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3030 boolean no_delay = (tape.warp_forward);
3031 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3032 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3033 unsigned int anim_delay = 0;
3035 int tile_size = MAX(request.step_offset, 1);
3036 int max_xsize = request.width / tile_size;
3037 int max_ysize = request.height / tile_size;
3038 int max_xsize_inner = max_xsize - 2;
3039 int max_ysize_inner = max_ysize - 2;
3041 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3042 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3043 int xend = max_xsize_inner;
3044 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3045 int xstep = (xstart < xend ? 1 : 0);
3046 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3048 int end = MAX(xend - xstart, yend - ystart);
3051 if (setup.quick_doors)
3058 for (i = start; i <= end; i++)
3060 int last_frame = end; // last frame of this "for" loop
3061 int x = xstart + i * xstep;
3062 int y = ystart + i * ystep;
3063 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3064 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3065 int xsize_size_left = (xsize - 1) * tile_size;
3066 int ysize_size_top = (ysize - 1) * tile_size;
3067 int max_xsize_pos = (max_xsize - 1) * tile_size;
3068 int max_ysize_pos = (max_ysize - 1) * tile_size;
3069 int width = xsize * tile_size;
3070 int height = ysize * tile_size;
3075 setRequestPosition(&src_x, &src_y, FALSE);
3076 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3078 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3080 for (yy = 0; yy < 2; yy++)
3082 for (xx = 0; xx < 2; xx++)
3084 int src_xx = src_x + xx * max_xsize_pos;
3085 int src_yy = src_y + yy * max_ysize_pos;
3086 int dst_xx = dst_x + xx * xsize_size_left;
3087 int dst_yy = dst_y + yy * ysize_size_top;
3088 int xx_size = (xx ? tile_size : xsize_size_left);
3089 int yy_size = (yy ? tile_size : ysize_size_top);
3092 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3093 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3095 BlitBitmap(bitmap_db_store_2, backbuffer,
3096 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3100 redraw_mask |= REDRAW_FIELD;
3104 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3107 ClearAutoRepeatKeyEvents();
3110 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3112 int graphic = IMG_BACKGROUND_REQUEST;
3113 int sound_opening = SND_REQUEST_OPENING;
3114 int sound_closing = SND_REQUEST_CLOSING;
3115 int anim_mode_1 = request.anim_mode; // (higher priority)
3116 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3117 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3118 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3119 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3121 if (game_status == GAME_MODE_PLAYING)
3122 BlitScreenToBitmap(backbuffer);
3124 SetDrawtoField(DRAW_TO_BACKBUFFER);
3126 // SetDrawBackgroundMask(REDRAW_NONE);
3128 if (action == ACTION_OPENING)
3130 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3132 if (req_state & REQ_ASK)
3134 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3135 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3137 else if (req_state & REQ_CONFIRM)
3139 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3141 else if (req_state & REQ_PLAYER)
3143 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3144 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3145 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3146 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3149 DrawEnvelopeRequest(text);
3152 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3154 if (action == ACTION_OPENING)
3156 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3158 if (anim_mode == ANIM_DEFAULT)
3159 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3161 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3165 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3167 if (anim_mode != ANIM_NONE)
3168 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3170 if (anim_mode == ANIM_DEFAULT)
3171 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3174 game.envelope_active = FALSE;
3176 if (action == ACTION_CLOSING)
3177 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3179 // SetDrawBackgroundMask(last_draw_background_mask);
3181 redraw_mask |= REDRAW_FIELD;
3185 if (action == ACTION_CLOSING &&
3186 game_status == GAME_MODE_PLAYING &&
3187 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3188 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3191 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3193 if (IS_MM_WALL(element))
3195 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3201 int graphic = el2preimg(element);
3203 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3204 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3209 void DrawLevel(int draw_background_mask)
3213 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3214 SetDrawBackgroundMask(draw_background_mask);
3218 for (x = BX1; x <= BX2; x++)
3219 for (y = BY1; y <= BY2; y++)
3220 DrawScreenField(x, y);
3222 redraw_mask |= REDRAW_FIELD;
3225 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3230 for (x = 0; x < size_x; x++)
3231 for (y = 0; y < size_y; y++)
3232 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3234 redraw_mask |= REDRAW_FIELD;
3237 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3241 for (x = 0; x < size_x; x++)
3242 for (y = 0; y < size_y; y++)
3243 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3245 redraw_mask |= REDRAW_FIELD;
3248 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3250 boolean show_level_border = (BorderElement != EL_EMPTY);
3251 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3252 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3253 int tile_size = preview.tile_size;
3254 int preview_width = preview.xsize * tile_size;
3255 int preview_height = preview.ysize * tile_size;
3256 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3257 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3258 int real_preview_width = real_preview_xsize * tile_size;
3259 int real_preview_height = real_preview_ysize * tile_size;
3260 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3261 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3264 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3267 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3269 dst_x += (preview_width - real_preview_width) / 2;
3270 dst_y += (preview_height - real_preview_height) / 2;
3272 for (x = 0; x < real_preview_xsize; x++)
3274 for (y = 0; y < real_preview_ysize; y++)
3276 int lx = from_x + x + (show_level_border ? -1 : 0);
3277 int ly = from_y + y + (show_level_border ? -1 : 0);
3278 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3279 getBorderElement(lx, ly));
3281 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3282 element, tile_size);
3286 redraw_mask |= REDRAW_FIELD;
3289 #define MICROLABEL_EMPTY 0
3290 #define MICROLABEL_LEVEL_NAME 1
3291 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3292 #define MICROLABEL_LEVEL_AUTHOR 3
3293 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3294 #define MICROLABEL_IMPORTED_FROM 5
3295 #define MICROLABEL_IMPORTED_BY_HEAD 6
3296 #define MICROLABEL_IMPORTED_BY 7
3298 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3300 int max_text_width = SXSIZE;
3301 int font_width = getFontWidth(font_nr);
3303 if (pos->align == ALIGN_CENTER)
3304 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3305 else if (pos->align == ALIGN_RIGHT)
3306 max_text_width = pos->x;
3308 max_text_width = SXSIZE - pos->x;
3310 return max_text_width / font_width;
3313 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3315 char label_text[MAX_OUTPUT_LINESIZE + 1];
3316 int max_len_label_text;
3317 int font_nr = pos->font;
3320 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3323 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3324 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3325 mode == MICROLABEL_IMPORTED_BY_HEAD)
3326 font_nr = pos->font_alt;
3328 max_len_label_text = getMaxTextLength(pos, font_nr);
3330 if (pos->size != -1)
3331 max_len_label_text = pos->size;
3333 for (i = 0; i < max_len_label_text; i++)
3334 label_text[i] = ' ';
3335 label_text[max_len_label_text] = '\0';
3337 if (strlen(label_text) > 0)
3338 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3341 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3342 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3343 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3344 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3345 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3346 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3347 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3348 max_len_label_text);
3349 label_text[max_len_label_text] = '\0';
3351 if (strlen(label_text) > 0)
3352 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3354 redraw_mask |= REDRAW_FIELD;
3357 static void DrawPreviewLevelLabel(int mode)
3359 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3362 static void DrawPreviewLevelInfo(int mode)
3364 if (mode == MICROLABEL_LEVEL_NAME)
3365 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3366 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3367 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3370 static void DrawPreviewLevelExt(boolean restart)
3372 static unsigned int scroll_delay = 0;
3373 static unsigned int label_delay = 0;
3374 static int from_x, from_y, scroll_direction;
3375 static int label_state, label_counter;
3376 unsigned int scroll_delay_value = preview.step_delay;
3377 boolean show_level_border = (BorderElement != EL_EMPTY);
3378 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3379 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3386 if (preview.anim_mode == ANIM_CENTERED)
3388 if (level_xsize > preview.xsize)
3389 from_x = (level_xsize - preview.xsize) / 2;
3390 if (level_ysize > preview.ysize)
3391 from_y = (level_ysize - preview.ysize) / 2;
3394 from_x += preview.xoffset;
3395 from_y += preview.yoffset;
3397 scroll_direction = MV_RIGHT;
3401 DrawPreviewLevelPlayfield(from_x, from_y);
3402 DrawPreviewLevelLabel(label_state);
3404 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3405 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3407 // initialize delay counters
3408 DelayReached(&scroll_delay, 0);
3409 DelayReached(&label_delay, 0);
3411 if (leveldir_current->name)
3413 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3414 char label_text[MAX_OUTPUT_LINESIZE + 1];
3415 int font_nr = pos->font;
3416 int max_len_label_text = getMaxTextLength(pos, font_nr);
3418 if (pos->size != -1)
3419 max_len_label_text = pos->size;
3421 strncpy(label_text, leveldir_current->name, max_len_label_text);
3422 label_text[max_len_label_text] = '\0';
3424 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3425 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3431 // scroll preview level, if needed
3432 if (preview.anim_mode != ANIM_NONE &&
3433 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3434 DelayReached(&scroll_delay, scroll_delay_value))
3436 switch (scroll_direction)
3441 from_x -= preview.step_offset;
3442 from_x = (from_x < 0 ? 0 : from_x);
3445 scroll_direction = MV_UP;
3449 if (from_x < level_xsize - preview.xsize)
3451 from_x += preview.step_offset;
3452 from_x = (from_x > level_xsize - preview.xsize ?
3453 level_xsize - preview.xsize : from_x);
3456 scroll_direction = MV_DOWN;
3462 from_y -= preview.step_offset;
3463 from_y = (from_y < 0 ? 0 : from_y);
3466 scroll_direction = MV_RIGHT;
3470 if (from_y < level_ysize - preview.ysize)
3472 from_y += preview.step_offset;
3473 from_y = (from_y > level_ysize - preview.ysize ?
3474 level_ysize - preview.ysize : from_y);
3477 scroll_direction = MV_LEFT;
3484 DrawPreviewLevelPlayfield(from_x, from_y);
3487 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3488 // redraw micro level label, if needed
3489 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3490 !strEqual(level.author, ANONYMOUS_NAME) &&
3491 !strEqual(level.author, leveldir_current->name) &&
3492 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3494 int max_label_counter = 23;
3496 if (leveldir_current->imported_from != NULL &&
3497 strlen(leveldir_current->imported_from) > 0)
3498 max_label_counter += 14;
3499 if (leveldir_current->imported_by != NULL &&
3500 strlen(leveldir_current->imported_by) > 0)
3501 max_label_counter += 14;
3503 label_counter = (label_counter + 1) % max_label_counter;
3504 label_state = (label_counter >= 0 && label_counter <= 7 ?
3505 MICROLABEL_LEVEL_NAME :
3506 label_counter >= 9 && label_counter <= 12 ?
3507 MICROLABEL_LEVEL_AUTHOR_HEAD :
3508 label_counter >= 14 && label_counter <= 21 ?
3509 MICROLABEL_LEVEL_AUTHOR :
3510 label_counter >= 23 && label_counter <= 26 ?
3511 MICROLABEL_IMPORTED_FROM_HEAD :
3512 label_counter >= 28 && label_counter <= 35 ?
3513 MICROLABEL_IMPORTED_FROM :
3514 label_counter >= 37 && label_counter <= 40 ?
3515 MICROLABEL_IMPORTED_BY_HEAD :
3516 label_counter >= 42 && label_counter <= 49 ?
3517 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3519 if (leveldir_current->imported_from == NULL &&
3520 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3521 label_state == MICROLABEL_IMPORTED_FROM))
3522 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3523 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3525 DrawPreviewLevelLabel(label_state);
3529 void DrawPreviewPlayers(void)
3531 if (game_status != GAME_MODE_MAIN)
3534 // do not draw preview players if level preview redefined, but players aren't
3535 if (preview.redefined && !menu.main.preview_players.redefined)
3538 boolean player_found[MAX_PLAYERS];
3539 int num_players = 0;
3542 for (i = 0; i < MAX_PLAYERS; i++)
3543 player_found[i] = FALSE;
3545 // check which players can be found in the level (simple approach)
3546 for (x = 0; x < lev_fieldx; x++)
3548 for (y = 0; y < lev_fieldy; y++)
3550 int element = level.field[x][y];
3552 if (ELEM_IS_PLAYER(element))
3554 int player_nr = GET_PLAYER_NR(element);
3556 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3558 if (!player_found[player_nr])
3561 player_found[player_nr] = TRUE;
3566 struct TextPosInfo *pos = &menu.main.preview_players;
3567 int tile_size = pos->tile_size;
3568 int border_size = pos->border_size;
3569 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3570 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3571 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3572 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3573 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3574 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3575 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3576 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3577 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3578 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3579 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3580 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3582 // clear area in which the players will be drawn
3583 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3584 max_players_width, max_players_height);
3586 if (!network.enabled && !setup.team_mode)
3589 // only draw players if level is suited for team mode
3590 if (num_players < 2)
3593 // draw all players that were found in the level
3594 for (i = 0; i < MAX_PLAYERS; i++)
3596 if (player_found[i])
3598 int graphic = el2img(EL_PLAYER_1 + i);
3600 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3602 xpos += player_xoffset;
3603 ypos += player_yoffset;
3608 void DrawPreviewLevelInitial(void)
3610 DrawPreviewLevelExt(TRUE);
3611 DrawPreviewPlayers();
3614 void DrawPreviewLevelAnimation(void)
3616 DrawPreviewLevelExt(FALSE);
3619 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3620 int border_size, int font_nr)
3622 int graphic = el2img(EL_PLAYER_1 + player_nr);
3623 int font_height = getFontHeight(font_nr);
3624 int player_height = MAX(tile_size, font_height);
3625 int xoffset_text = tile_size + border_size;
3626 int yoffset_text = (player_height - font_height) / 2;
3627 int yoffset_graphic = (player_height - tile_size) / 2;
3628 char *player_name = getNetworkPlayerName(player_nr + 1);
3630 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3632 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3635 static void DrawNetworkPlayersExt(boolean force)
3637 if (game_status != GAME_MODE_MAIN)
3640 if (!network.connected && !force)
3643 // do not draw network players if level preview redefined, but players aren't
3644 if (preview.redefined && !menu.main.network_players.redefined)
3647 int num_players = 0;
3650 for (i = 0; i < MAX_PLAYERS; i++)
3651 if (stored_player[i].connected_network)
3654 struct TextPosInfo *pos = &menu.main.network_players;
3655 int tile_size = pos->tile_size;
3656 int border_size = pos->border_size;
3657 int xoffset_text = tile_size + border_size;
3658 int font_nr = pos->font;
3659 int font_width = getFontWidth(font_nr);
3660 int font_height = getFontHeight(font_nr);
3661 int player_height = MAX(tile_size, font_height);
3662 int player_yoffset = player_height + border_size;
3663 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3664 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3665 int all_players_height = num_players * player_yoffset - border_size;
3666 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3667 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3668 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3670 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3671 max_players_width, max_players_height);
3673 // first draw local network player ...
3674 for (i = 0; i < MAX_PLAYERS; i++)
3676 if (stored_player[i].connected_network &&
3677 stored_player[i].connected_locally)
3679 char *player_name = getNetworkPlayerName(i + 1);
3680 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3681 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3683 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3685 ypos += player_yoffset;
3689 // ... then draw all other network players
3690 for (i = 0; i < MAX_PLAYERS; i++)
3692 if (stored_player[i].connected_network &&
3693 !stored_player[i].connected_locally)
3695 char *player_name = getNetworkPlayerName(i + 1);
3696 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3697 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3699 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3701 ypos += player_yoffset;
3706 void DrawNetworkPlayers(void)
3708 DrawNetworkPlayersExt(FALSE);
3711 void ClearNetworkPlayers(void)
3713 DrawNetworkPlayersExt(TRUE);
3716 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3717 int graphic, int sync_frame,
3720 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3722 if (mask_mode == USE_MASKING)
3723 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3725 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3728 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3729 int graphic, int sync_frame, int mask_mode)
3731 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3733 if (mask_mode == USE_MASKING)
3734 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3736 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3739 static void DrawGraphicAnimation(int x, int y, int graphic)
3741 int lx = LEVELX(x), ly = LEVELY(y);
3743 if (!IN_SCR_FIELD(x, y))
3746 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3747 graphic, GfxFrame[lx][ly], NO_MASKING);
3749 MarkTileDirty(x, y);
3752 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3754 int lx = LEVELX(x), ly = LEVELY(y);
3756 if (!IN_SCR_FIELD(x, y))
3759 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3760 graphic, GfxFrame[lx][ly], NO_MASKING);
3761 MarkTileDirty(x, y);
3764 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3766 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3769 void DrawLevelElementAnimation(int x, int y, int element)
3771 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3773 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3776 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3778 int sx = SCREENX(x), sy = SCREENY(y);
3780 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3783 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3786 DrawGraphicAnimation(sx, sy, graphic);
3789 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3790 DrawLevelFieldCrumbled(x, y);
3792 if (GFX_CRUMBLED(Feld[x][y]))
3793 DrawLevelFieldCrumbled(x, y);
3797 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3799 int sx = SCREENX(x), sy = SCREENY(y);
3802 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3805 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3807 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3810 DrawGraphicAnimation(sx, sy, graphic);
3812 if (GFX_CRUMBLED(element))
3813 DrawLevelFieldCrumbled(x, y);
3816 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3818 if (player->use_murphy)
3820 // this works only because currently only one player can be "murphy" ...
3821 static int last_horizontal_dir = MV_LEFT;
3822 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3824 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3825 last_horizontal_dir = move_dir;
3827 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3829 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3831 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3837 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3840 static boolean equalGraphics(int graphic1, int graphic2)
3842 struct GraphicInfo *g1 = &graphic_info[graphic1];
3843 struct GraphicInfo *g2 = &graphic_info[graphic2];
3845 return (g1->bitmap == g2->bitmap &&
3846 g1->src_x == g2->src_x &&
3847 g1->src_y == g2->src_y &&
3848 g1->anim_frames == g2->anim_frames &&
3849 g1->anim_delay == g2->anim_delay &&
3850 g1->anim_mode == g2->anim_mode);
3853 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3857 DRAW_PLAYER_STAGE_INIT = 0,
3858 DRAW_PLAYER_STAGE_LAST_FIELD,
3859 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3860 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3861 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3862 DRAW_PLAYER_STAGE_PLAYER,
3864 DRAW_PLAYER_STAGE_PLAYER,
3865 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3867 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3868 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3870 NUM_DRAW_PLAYER_STAGES
3873 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3875 static int static_last_player_graphic[MAX_PLAYERS];
3876 static int static_last_player_frame[MAX_PLAYERS];
3877 static boolean static_player_is_opaque[MAX_PLAYERS];
3878 static boolean draw_player[MAX_PLAYERS];
3879 int pnr = player->index_nr;
3881 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3883 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3884 static_last_player_frame[pnr] = player->Frame;
3885 static_player_is_opaque[pnr] = FALSE;
3887 draw_player[pnr] = TRUE;
3890 if (!draw_player[pnr])
3894 if (!IN_LEV_FIELD(player->jx, player->jy))
3896 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3897 printf("DrawPlayerField(): This should never happen!\n");
3899 draw_player[pnr] = FALSE;
3905 int last_player_graphic = static_last_player_graphic[pnr];
3906 int last_player_frame = static_last_player_frame[pnr];
3907 boolean player_is_opaque = static_player_is_opaque[pnr];
3909 int jx = player->jx;
3910 int jy = player->jy;
3911 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3912 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3913 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3914 int last_jx = (player->is_moving ? jx - dx : jx);
3915 int last_jy = (player->is_moving ? jy - dy : jy);
3916 int next_jx = jx + dx;
3917 int next_jy = jy + dy;
3918 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3919 int sx = SCREENX(jx);
3920 int sy = SCREENY(jy);
3921 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3922 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3923 int element = Feld[jx][jy];
3924 int last_element = Feld[last_jx][last_jy];
3925 int action = (player->is_pushing ? ACTION_PUSHING :
3926 player->is_digging ? ACTION_DIGGING :
3927 player->is_collecting ? ACTION_COLLECTING :
3928 player->is_moving ? ACTION_MOVING :
3929 player->is_snapping ? ACTION_SNAPPING :
3930 player->is_dropping ? ACTION_DROPPING :
3931 player->is_waiting ? player->action_waiting :
3934 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3936 // ------------------------------------------------------------------------
3937 // initialize drawing the player
3938 // ------------------------------------------------------------------------
3940 draw_player[pnr] = FALSE;
3942 // GfxElement[][] is set to the element the player is digging or collecting;
3943 // remove also for off-screen player if the player is not moving anymore
3944 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3945 GfxElement[jx][jy] = EL_UNDEFINED;
3947 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3950 if (element == EL_EXPLOSION)
3953 InitPlayerGfxAnimation(player, action, move_dir);
3955 draw_player[pnr] = TRUE;
3957 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3959 // ------------------------------------------------------------------------
3960 // draw things in the field the player is leaving, if needed
3961 // ------------------------------------------------------------------------
3963 if (!IN_SCR_FIELD(sx, sy))
3964 draw_player[pnr] = FALSE;
3966 if (!player->is_moving)
3969 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3971 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3973 if (last_element == EL_DYNAMITE_ACTIVE ||
3974 last_element == EL_EM_DYNAMITE_ACTIVE ||
3975 last_element == EL_SP_DISK_RED_ACTIVE)
3976 DrawDynamite(last_jx, last_jy);
3978 DrawLevelFieldThruMask(last_jx, last_jy);
3980 else if (last_element == EL_DYNAMITE_ACTIVE ||
3981 last_element == EL_EM_DYNAMITE_ACTIVE ||
3982 last_element == EL_SP_DISK_RED_ACTIVE)
3983 DrawDynamite(last_jx, last_jy);
3985 DrawLevelField(last_jx, last_jy);
3987 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3988 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3990 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3992 // ------------------------------------------------------------------------
3993 // draw things behind the player, if needed
3994 // ------------------------------------------------------------------------
3998 DrawLevelElement(jx, jy, Back[jx][jy]);
4003 if (IS_ACTIVE_BOMB(element))
4005 DrawLevelElement(jx, jy, EL_EMPTY);
4010 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4012 int old_element = GfxElement[jx][jy];
4013 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4014 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4016 if (GFX_CRUMBLED(old_element))
4017 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4019 DrawGraphic(sx, sy, old_graphic, frame);
4021 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4022 static_player_is_opaque[pnr] = TRUE;
4026 GfxElement[jx][jy] = EL_UNDEFINED;
4028 // make sure that pushed elements are drawn with correct frame rate
4029 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4031 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4032 GfxFrame[jx][jy] = player->StepFrame;
4034 DrawLevelField(jx, jy);
4037 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4039 // ------------------------------------------------------------------------
4040 // draw things the player is pushing, if needed
4041 // ------------------------------------------------------------------------
4043 if (!player->is_pushing || !player->is_moving)
4046 int gfx_frame = GfxFrame[jx][jy];
4048 if (!IS_MOVING(jx, jy)) // push movement already finished
4050 element = Feld[next_jx][next_jy];
4051 gfx_frame = GfxFrame[next_jx][next_jy];
4054 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4055 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4056 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4058 // draw background element under pushed element (like the Sokoban field)
4059 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4061 // this allows transparent pushing animation over non-black background
4064 DrawLevelElement(jx, jy, Back[jx][jy]);
4066 DrawLevelElement(jx, jy, EL_EMPTY);
4068 if (Back[next_jx][next_jy])
4069 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4071 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4073 else if (Back[next_jx][next_jy])
4074 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4076 int px = SCREENX(jx), py = SCREENY(jy);
4077 int pxx = (TILEX - ABS(sxx)) * dx;
4078 int pyy = (TILEY - ABS(syy)) * dy;
4081 // do not draw (EM style) pushing animation when pushing is finished
4082 // (two-tile animations usually do not contain start and end frame)
4083 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4084 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4086 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4088 // masked drawing is needed for EMC style (double) movement graphics
4089 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4090 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4093 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4095 // ------------------------------------------------------------------------
4096 // draw player himself
4097 // ------------------------------------------------------------------------
4099 int graphic = getPlayerGraphic(player, move_dir);
4101 // in the case of changed player action or direction, prevent the current
4102 // animation frame from being restarted for identical animations
4103 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4104 player->Frame = last_player_frame;
4106 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4108 if (player_is_opaque)
4109 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4111 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4113 if (SHIELD_ON(player))
4115 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4116 IMG_SHIELD_NORMAL_ACTIVE);
4117 frame = getGraphicAnimationFrame(graphic, -1);
4119 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4122 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4124 // ------------------------------------------------------------------------
4125 // draw things in front of player (active dynamite or dynabombs)
4126 // ------------------------------------------------------------------------
4128 if (IS_ACTIVE_BOMB(element))
4130 int graphic = el2img(element);
4131 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4133 if (game.emulation == EMU_SUPAPLEX)
4134 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4136 DrawGraphicThruMask(sx, sy, graphic, frame);
4139 if (player_is_moving && last_element == EL_EXPLOSION)
4141 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4142 GfxElement[last_jx][last_jy] : EL_EMPTY);
4143 int graphic = el_act2img(element, ACTION_EXPLODING);
4144 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4145 int phase = ExplodePhase[last_jx][last_jy] - 1;
4146 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4149 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4152 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4154 // ------------------------------------------------------------------------
4155 // draw elements the player is just walking/passing through/under
4156 // ------------------------------------------------------------------------
4158 if (player_is_moving)
4160 // handle the field the player is leaving ...
4161 if (IS_ACCESSIBLE_INSIDE(last_element))
4162 DrawLevelField(last_jx, last_jy);
4163 else if (IS_ACCESSIBLE_UNDER(last_element))
4164 DrawLevelFieldThruMask(last_jx, last_jy);
4167 // do not redraw accessible elements if the player is just pushing them
4168 if (!player_is_moving || !player->is_pushing)
4170 // ... and the field the player is entering
4171 if (IS_ACCESSIBLE_INSIDE(element))
4172 DrawLevelField(jx, jy);
4173 else if (IS_ACCESSIBLE_UNDER(element))
4174 DrawLevelFieldThruMask(jx, jy);
4177 MarkTileDirty(sx, sy);
4181 void DrawPlayer(struct PlayerInfo *player)
4185 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4186 DrawPlayerExt(player, i);
4189 void DrawAllPlayers(void)
4193 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4194 for (j = 0; j < MAX_PLAYERS; j++)
4195 if (stored_player[j].active)
4196 DrawPlayerExt(&stored_player[j], i);
4199 void DrawPlayerField(int x, int y)
4201 if (!IS_PLAYER(x, y))
4204 DrawPlayer(PLAYERINFO(x, y));
4207 // ----------------------------------------------------------------------------
4209 void WaitForEventToContinue(void)
4211 boolean still_wait = TRUE;
4213 if (program.headless)
4216 // simulate releasing mouse button over last gadget, if still pressed
4218 HandleGadgets(-1, -1, 0);
4220 button_status = MB_RELEASED;
4228 if (NextValidEvent(&event))
4232 case EVENT_BUTTONRELEASE:
4233 case EVENT_KEYPRESS:
4234 case SDL_CONTROLLERBUTTONDOWN:
4235 case SDL_JOYBUTTONDOWN:
4239 case EVENT_KEYRELEASE:
4240 ClearPlayerAction();
4244 HandleOtherEvents(&event);
4248 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4257 #define MAX_REQUEST_LINES 13
4258 #define MAX_REQUEST_LINE_FONT1_LEN 7
4259 #define MAX_REQUEST_LINE_FONT2_LEN 10
4261 static int RequestHandleEvents(unsigned int req_state)
4263 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4265 int width = request.width;
4266 int height = request.height;
4270 // when showing request dialog after game ended, deactivate game panel
4271 if (game_just_ended)
4272 game.panel.active = FALSE;
4274 game.request_active = TRUE;
4276 setRequestPosition(&sx, &sy, FALSE);
4278 button_status = MB_RELEASED;
4280 request_gadget_id = -1;
4285 if (game_just_ended)
4287 // the MM game engine does not use a special (scrollable) field buffer
4288 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4289 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4291 HandleGameActions();
4293 SetDrawtoField(DRAW_TO_BACKBUFFER);
4295 if (global.use_envelope_request)
4297 // copy current state of request area to middle of playfield area
4298 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4306 while (NextValidEvent(&event))
4310 case EVENT_BUTTONPRESS:
4311 case EVENT_BUTTONRELEASE:
4312 case EVENT_MOTIONNOTIFY:
4316 if (event.type == EVENT_MOTIONNOTIFY)
4321 motion_status = TRUE;
4322 mx = ((MotionEvent *) &event)->x;
4323 my = ((MotionEvent *) &event)->y;
4327 motion_status = FALSE;
4328 mx = ((ButtonEvent *) &event)->x;
4329 my = ((ButtonEvent *) &event)->y;
4330 if (event.type == EVENT_BUTTONPRESS)
4331 button_status = ((ButtonEvent *) &event)->button;
4333 button_status = MB_RELEASED;
4336 if (HandleGlobalAnimClicks(mx, my, button_status, FALSE))
4338 // do not handle this button event anymore
4339 continue; // force mouse event not to be handled at all
4342 // this sets 'request_gadget_id'
4343 HandleGadgets(mx, my, button_status);
4345 switch (request_gadget_id)
4347 case TOOL_CTRL_ID_YES:
4350 case TOOL_CTRL_ID_NO:
4353 case TOOL_CTRL_ID_CONFIRM:
4354 result = TRUE | FALSE;
4357 case TOOL_CTRL_ID_PLAYER_1:
4360 case TOOL_CTRL_ID_PLAYER_2:
4363 case TOOL_CTRL_ID_PLAYER_3:
4366 case TOOL_CTRL_ID_PLAYER_4:
4377 case SDL_WINDOWEVENT:
4378 HandleWindowEvent((WindowEvent *) &event);
4381 case SDL_APP_WILLENTERBACKGROUND:
4382 case SDL_APP_DIDENTERBACKGROUND:
4383 case SDL_APP_WILLENTERFOREGROUND:
4384 case SDL_APP_DIDENTERFOREGROUND:
4385 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4388 case EVENT_KEYPRESS:
4390 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4395 if (req_state & REQ_CONFIRM)
4404 #if defined(KSYM_Rewind)
4405 case KSYM_Rewind: // for Amazon Fire TV remote
4414 #if defined(KSYM_FastForward)
4415 case KSYM_FastForward: // for Amazon Fire TV remote
4421 HandleKeysDebug(key, KEY_PRESSED);
4425 if (req_state & REQ_PLAYER)
4427 int old_player_nr = setup.network_player_nr;
4430 result = old_player_nr + 1;
4435 result = old_player_nr + 1;
4466 case EVENT_KEYRELEASE:
4467 ClearPlayerAction();
4470 case SDL_CONTROLLERBUTTONDOWN:
4471 switch (event.cbutton.button)
4473 case SDL_CONTROLLER_BUTTON_A:
4474 case SDL_CONTROLLER_BUTTON_X:
4475 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4476 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4480 case SDL_CONTROLLER_BUTTON_B:
4481 case SDL_CONTROLLER_BUTTON_Y:
4482 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4483 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4484 case SDL_CONTROLLER_BUTTON_BACK:
4489 if (req_state & REQ_PLAYER)
4491 int old_player_nr = setup.network_player_nr;
4494 result = old_player_nr + 1;
4496 switch (event.cbutton.button)
4498 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4499 case SDL_CONTROLLER_BUTTON_Y:
4503 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4504 case SDL_CONTROLLER_BUTTON_B:
4508 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4509 case SDL_CONTROLLER_BUTTON_A:
4513 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4514 case SDL_CONTROLLER_BUTTON_X:
4525 case SDL_CONTROLLERBUTTONUP:
4526 HandleJoystickEvent(&event);
4527 ClearPlayerAction();
4531 HandleOtherEvents(&event);
4536 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4538 int joy = AnyJoystick();
4540 if (joy & JOY_BUTTON_1)
4542 else if (joy & JOY_BUTTON_2)
4545 else if (AnyJoystick())
4547 int joy = AnyJoystick();
4549 if (req_state & REQ_PLAYER)
4553 else if (joy & JOY_RIGHT)
4555 else if (joy & JOY_DOWN)
4557 else if (joy & JOY_LEFT)
4562 if (game_just_ended)
4564 if (global.use_envelope_request)
4566 // copy back current state of pressed buttons inside request area
4567 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4574 game.request_active = FALSE;
4579 static boolean RequestDoor(char *text, unsigned int req_state)
4581 unsigned int old_door_state;
4582 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4583 int font_nr = FONT_TEXT_2;
4588 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4590 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4591 font_nr = FONT_TEXT_1;
4594 if (game_status == GAME_MODE_PLAYING)
4595 BlitScreenToBitmap(backbuffer);
4597 // disable deactivated drawing when quick-loading level tape recording
4598 if (tape.playing && tape.deactivate_display)
4599 TapeDeactivateDisplayOff(TRUE);
4601 SetMouseCursor(CURSOR_DEFAULT);
4603 // pause network game while waiting for request to answer
4604 if (network.enabled &&
4605 game_status == GAME_MODE_PLAYING &&
4606 !game.all_players_gone &&
4607 req_state & REQUEST_WAIT_FOR_INPUT)
4608 SendToServer_PausePlaying();
4610 old_door_state = GetDoorState();
4612 // simulate releasing mouse button over last gadget, if still pressed
4614 HandleGadgets(-1, -1, 0);
4618 // draw released gadget before proceeding
4621 if (old_door_state & DOOR_OPEN_1)
4623 CloseDoor(DOOR_CLOSE_1);
4625 // save old door content
4626 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4627 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4630 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4631 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4633 // clear door drawing field
4634 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4636 // force DOOR font inside door area
4637 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4639 // write text for request
4640 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4642 char text_line[max_request_line_len + 1];
4648 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4650 tc = *(text_ptr + tx);
4651 // if (!tc || tc == ' ')
4652 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4656 if ((tc == '?' || tc == '!') && tl == 0)
4666 strncpy(text_line, text_ptr, tl);
4669 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4670 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4671 text_line, font_nr);
4673 text_ptr += tl + (tc == ' ' ? 1 : 0);
4674 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4679 if (req_state & REQ_ASK)
4681 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4682 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4684 else if (req_state & REQ_CONFIRM)
4686 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4688 else if (req_state & REQ_PLAYER)
4690 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4691 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4692 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4693 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4696 // copy request gadgets to door backbuffer
4697 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4699 OpenDoor(DOOR_OPEN_1);
4701 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4703 if (game_status == GAME_MODE_PLAYING)
4705 SetPanelBackground();
4706 SetDrawBackgroundMask(REDRAW_DOOR_1);
4710 SetDrawBackgroundMask(REDRAW_FIELD);
4716 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4718 // ---------- handle request buttons ----------
4719 result = RequestHandleEvents(req_state);
4723 if (!(req_state & REQ_STAY_OPEN))
4725 CloseDoor(DOOR_CLOSE_1);
4727 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4728 (req_state & REQ_REOPEN))
4729 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4734 if (game_status == GAME_MODE_PLAYING)
4736 SetPanelBackground();
4737 SetDrawBackgroundMask(REDRAW_DOOR_1);
4741 SetDrawBackgroundMask(REDRAW_FIELD);
4744 // continue network game after request
4745 if (network.enabled &&
4746 game_status == GAME_MODE_PLAYING &&
4747 !game.all_players_gone &&
4748 req_state & REQUEST_WAIT_FOR_INPUT)
4749 SendToServer_ContinuePlaying();
4751 // restore deactivated drawing when quick-loading level tape recording
4752 if (tape.playing && tape.deactivate_display)
4753 TapeDeactivateDisplayOn();
4758 static boolean RequestEnvelope(char *text, unsigned int req_state)
4762 if (game_status == GAME_MODE_PLAYING)
4763 BlitScreenToBitmap(backbuffer);
4765 // disable deactivated drawing when quick-loading level tape recording
4766 if (tape.playing && tape.deactivate_display)
4767 TapeDeactivateDisplayOff(TRUE);
4769 SetMouseCursor(CURSOR_DEFAULT);
4771 // pause network game while waiting for request to answer
4772 if (network.enabled &&
4773 game_status == GAME_MODE_PLAYING &&
4774 !game.all_players_gone &&
4775 req_state & REQUEST_WAIT_FOR_INPUT)
4776 SendToServer_PausePlaying();
4778 // simulate releasing mouse button over last gadget, if still pressed
4780 HandleGadgets(-1, -1, 0);
4784 // (replace with setting corresponding request background)
4785 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4786 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4788 // clear door drawing field
4789 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4791 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4793 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4795 if (game_status == GAME_MODE_PLAYING)
4797 SetPanelBackground();
4798 SetDrawBackgroundMask(REDRAW_DOOR_1);
4802 SetDrawBackgroundMask(REDRAW_FIELD);
4808 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4810 // ---------- handle request buttons ----------
4811 result = RequestHandleEvents(req_state);
4815 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4819 if (game_status == GAME_MODE_PLAYING)
4821 SetPanelBackground();
4822 SetDrawBackgroundMask(REDRAW_DOOR_1);
4826 SetDrawBackgroundMask(REDRAW_FIELD);
4829 // continue network game after request
4830 if (network.enabled &&
4831 game_status == GAME_MODE_PLAYING &&
4832 !game.all_players_gone &&
4833 req_state & REQUEST_WAIT_FOR_INPUT)
4834 SendToServer_ContinuePlaying();
4836 // restore deactivated drawing when quick-loading level tape recording
4837 if (tape.playing && tape.deactivate_display)
4838 TapeDeactivateDisplayOn();
4843 boolean Request(char *text, unsigned int req_state)
4845 boolean overlay_enabled = GetOverlayEnabled();
4848 SetOverlayEnabled(FALSE);
4850 if (global.use_envelope_request)
4851 result = RequestEnvelope(text, req_state);
4853 result = RequestDoor(text, req_state);
4855 SetOverlayEnabled(overlay_enabled);
4860 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4862 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4863 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4866 if (dpo1->sort_priority != dpo2->sort_priority)
4867 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4869 compare_result = dpo1->nr - dpo2->nr;
4871 return compare_result;
4874 void InitGraphicCompatibilityInfo_Doors(void)
4880 struct DoorInfo *door;
4884 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4885 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4887 { -1, -1, -1, NULL }
4889 struct Rect door_rect_list[] =
4891 { DX, DY, DXSIZE, DYSIZE },
4892 { VX, VY, VXSIZE, VYSIZE }
4896 for (i = 0; doors[i].door_token != -1; i++)
4898 int door_token = doors[i].door_token;
4899 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4900 int part_1 = doors[i].part_1;
4901 int part_8 = doors[i].part_8;
4902 int part_2 = part_1 + 1;
4903 int part_3 = part_1 + 2;
4904 struct DoorInfo *door = doors[i].door;
4905 struct Rect *door_rect = &door_rect_list[door_index];
4906 boolean door_gfx_redefined = FALSE;
4908 // check if any door part graphic definitions have been redefined
4910 for (j = 0; door_part_controls[j].door_token != -1; j++)
4912 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4913 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4915 if (dpc->door_token == door_token && fi->redefined)
4916 door_gfx_redefined = TRUE;
4919 // check for old-style door graphic/animation modifications
4921 if (!door_gfx_redefined)
4923 if (door->anim_mode & ANIM_STATIC_PANEL)
4925 door->panel.step_xoffset = 0;
4926 door->panel.step_yoffset = 0;
4929 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4931 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4932 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4933 int num_door_steps, num_panel_steps;
4935 // remove door part graphics other than the two default wings
4937 for (j = 0; door_part_controls[j].door_token != -1; j++)
4939 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4940 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4942 if (dpc->graphic >= part_3 &&
4943 dpc->graphic <= part_8)
4947 // set graphics and screen positions of the default wings
4949 g_part_1->width = door_rect->width;
4950 g_part_1->height = door_rect->height;
4951 g_part_2->width = door_rect->width;
4952 g_part_2->height = door_rect->height;
4953 g_part_2->src_x = door_rect->width;
4954 g_part_2->src_y = g_part_1->src_y;
4956 door->part_2.x = door->part_1.x;
4957 door->part_2.y = door->part_1.y;
4959 if (door->width != -1)
4961 g_part_1->width = door->width;
4962 g_part_2->width = door->width;
4964 // special treatment for graphics and screen position of right wing
4965 g_part_2->src_x += door_rect->width - door->width;
4966 door->part_2.x += door_rect->width - door->width;
4969 if (door->height != -1)
4971 g_part_1->height = door->height;
4972 g_part_2->height = door->height;
4974 // special treatment for graphics and screen position of bottom wing
4975 g_part_2->src_y += door_rect->height - door->height;
4976 door->part_2.y += door_rect->height - door->height;
4979 // set animation delays for the default wings and panels
4981 door->part_1.step_delay = door->step_delay;
4982 door->part_2.step_delay = door->step_delay;
4983 door->panel.step_delay = door->step_delay;
4985 // set animation draw order for the default wings
4987 door->part_1.sort_priority = 2; // draw left wing over ...
4988 door->part_2.sort_priority = 1; // ... right wing
4990 // set animation draw offset for the default wings
4992 if (door->anim_mode & ANIM_HORIZONTAL)
4994 door->part_1.step_xoffset = door->step_offset;
4995 door->part_1.step_yoffset = 0;
4996 door->part_2.step_xoffset = door->step_offset * -1;
4997 door->part_2.step_yoffset = 0;
4999 num_door_steps = g_part_1->width / door->step_offset;
5001 else // ANIM_VERTICAL
5003 door->part_1.step_xoffset = 0;
5004 door->part_1.step_yoffset = door->step_offset;
5005 door->part_2.step_xoffset = 0;
5006 door->part_2.step_yoffset = door->step_offset * -1;
5008 num_door_steps = g_part_1->height / door->step_offset;
5011 // set animation draw offset for the default panels
5013 if (door->step_offset > 1)
5015 num_panel_steps = 2 * door_rect->height / door->step_offset;
5016 door->panel.start_step = num_panel_steps - num_door_steps;
5017 door->panel.start_step_closing = door->panel.start_step;
5021 num_panel_steps = door_rect->height / door->step_offset;
5022 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5023 door->panel.start_step_closing = door->panel.start_step;
5024 door->panel.step_delay *= 2;
5031 void InitDoors(void)
5035 for (i = 0; door_part_controls[i].door_token != -1; i++)
5037 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5038 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5040 // initialize "start_step_opening" and "start_step_closing", if needed
5041 if (dpc->pos->start_step_opening == 0 &&
5042 dpc->pos->start_step_closing == 0)
5044 // dpc->pos->start_step_opening = dpc->pos->start_step;
5045 dpc->pos->start_step_closing = dpc->pos->start_step;
5048 // fill structure for door part draw order (sorted below)
5050 dpo->sort_priority = dpc->pos->sort_priority;
5053 // sort door part controls according to sort_priority and graphic number
5054 qsort(door_part_order, MAX_DOOR_PARTS,
5055 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5058 unsigned int OpenDoor(unsigned int door_state)
5060 if (door_state & DOOR_COPY_BACK)
5062 if (door_state & DOOR_OPEN_1)
5063 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5064 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5066 if (door_state & DOOR_OPEN_2)
5067 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5068 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5070 door_state &= ~DOOR_COPY_BACK;
5073 return MoveDoor(door_state);
5076 unsigned int CloseDoor(unsigned int door_state)
5078 unsigned int old_door_state = GetDoorState();
5080 if (!(door_state & DOOR_NO_COPY_BACK))
5082 if (old_door_state & DOOR_OPEN_1)
5083 BlitBitmap(backbuffer, bitmap_db_door_1,
5084 DX, DY, DXSIZE, DYSIZE, 0, 0);
5086 if (old_door_state & DOOR_OPEN_2)
5087 BlitBitmap(backbuffer, bitmap_db_door_2,
5088 VX, VY, VXSIZE, VYSIZE, 0, 0);
5090 door_state &= ~DOOR_NO_COPY_BACK;
5093 return MoveDoor(door_state);
5096 unsigned int GetDoorState(void)
5098 return MoveDoor(DOOR_GET_STATE);
5101 unsigned int SetDoorState(unsigned int door_state)
5103 return MoveDoor(door_state | DOOR_SET_STATE);
5106 static int euclid(int a, int b)
5108 return (b ? euclid(b, a % b) : a);
5111 unsigned int MoveDoor(unsigned int door_state)
5113 struct Rect door_rect_list[] =
5115 { DX, DY, DXSIZE, DYSIZE },
5116 { VX, VY, VXSIZE, VYSIZE }
5118 static int door1 = DOOR_CLOSE_1;
5119 static int door2 = DOOR_CLOSE_2;
5120 unsigned int door_delay = 0;
5121 unsigned int door_delay_value;
5124 if (door_state == DOOR_GET_STATE)
5125 return (door1 | door2);
5127 if (door_state & DOOR_SET_STATE)
5129 if (door_state & DOOR_ACTION_1)
5130 door1 = door_state & DOOR_ACTION_1;
5131 if (door_state & DOOR_ACTION_2)
5132 door2 = door_state & DOOR_ACTION_2;
5134 return (door1 | door2);
5137 if (!(door_state & DOOR_FORCE_REDRAW))
5139 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5140 door_state &= ~DOOR_OPEN_1;
5141 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5142 door_state &= ~DOOR_CLOSE_1;
5143 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5144 door_state &= ~DOOR_OPEN_2;
5145 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5146 door_state &= ~DOOR_CLOSE_2;
5149 if (global.autoplay_leveldir)
5151 door_state |= DOOR_NO_DELAY;
5152 door_state &= ~DOOR_CLOSE_ALL;
5155 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5156 door_state |= DOOR_NO_DELAY;
5158 if (door_state & DOOR_ACTION)
5160 boolean door_panel_drawn[NUM_DOORS];
5161 boolean panel_has_doors[NUM_DOORS];
5162 boolean door_part_skip[MAX_DOOR_PARTS];
5163 boolean door_part_done[MAX_DOOR_PARTS];
5164 boolean door_part_done_all;
5165 int num_steps[MAX_DOOR_PARTS];
5166 int max_move_delay = 0; // delay for complete animations of all doors
5167 int max_step_delay = 0; // delay (ms) between two animation frames
5168 int num_move_steps = 0; // number of animation steps for all doors
5169 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5170 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5171 int current_move_delay = 0;
5175 for (i = 0; i < NUM_DOORS; i++)
5176 panel_has_doors[i] = FALSE;
5178 for (i = 0; i < MAX_DOOR_PARTS; i++)
5180 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5181 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5182 int door_token = dpc->door_token;
5184 door_part_done[i] = FALSE;
5185 door_part_skip[i] = (!(door_state & door_token) ||
5189 for (i = 0; i < MAX_DOOR_PARTS; i++)
5191 int nr = door_part_order[i].nr;
5192 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5193 struct DoorPartPosInfo *pos = dpc->pos;
5194 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5195 int door_token = dpc->door_token;
5196 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5197 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5198 int step_xoffset = ABS(pos->step_xoffset);
5199 int step_yoffset = ABS(pos->step_yoffset);
5200 int step_delay = pos->step_delay;
5201 int current_door_state = door_state & door_token;
5202 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5203 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5204 boolean part_opening = (is_panel ? door_closing : door_opening);
5205 int start_step = (part_opening ? pos->start_step_opening :
5206 pos->start_step_closing);
5207 float move_xsize = (step_xoffset ? g->width : 0);
5208 float move_ysize = (step_yoffset ? g->height : 0);
5209 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5210 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5211 int move_steps = (move_xsteps && move_ysteps ?
5212 MIN(move_xsteps, move_ysteps) :
5213 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5214 int move_delay = move_steps * step_delay;
5216 if (door_part_skip[nr])
5219 max_move_delay = MAX(max_move_delay, move_delay);
5220 max_step_delay = (max_step_delay == 0 ? step_delay :
5221 euclid(max_step_delay, step_delay));
5222 num_steps[nr] = move_steps;
5226 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5228 panel_has_doors[door_index] = TRUE;
5232 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5234 num_move_steps = max_move_delay / max_step_delay;
5235 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5237 door_delay_value = max_step_delay;
5239 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5241 start = num_move_steps - 1;
5245 // opening door sound has priority over simultaneously closing door
5246 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5248 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5250 if (door_state & DOOR_OPEN_1)
5251 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5252 if (door_state & DOOR_OPEN_2)
5253 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5255 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5257 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5259 if (door_state & DOOR_CLOSE_1)
5260 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5261 if (door_state & DOOR_CLOSE_2)
5262 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5266 for (k = start; k < num_move_steps; k++)
5268 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5270 door_part_done_all = TRUE;
5272 for (i = 0; i < NUM_DOORS; i++)
5273 door_panel_drawn[i] = FALSE;
5275 for (i = 0; i < MAX_DOOR_PARTS; i++)
5277 int nr = door_part_order[i].nr;
5278 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5279 struct DoorPartPosInfo *pos = dpc->pos;
5280 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5281 int door_token = dpc->door_token;
5282 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5283 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5284 boolean is_panel_and_door_has_closed = FALSE;
5285 struct Rect *door_rect = &door_rect_list[door_index];
5286 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5288 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5289 int current_door_state = door_state & door_token;
5290 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5291 boolean door_closing = !door_opening;
5292 boolean part_opening = (is_panel ? door_closing : door_opening);
5293 boolean part_closing = !part_opening;
5294 int start_step = (part_opening ? pos->start_step_opening :
5295 pos->start_step_closing);
5296 int step_delay = pos->step_delay;
5297 int step_factor = step_delay / max_step_delay;
5298 int k1 = (step_factor ? k / step_factor + 1 : k);
5299 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5300 int kk = MAX(0, k2);
5303 int src_x, src_y, src_xx, src_yy;
5304 int dst_x, dst_y, dst_xx, dst_yy;
5307 if (door_part_skip[nr])
5310 if (!(door_state & door_token))
5318 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5319 int kk_door = MAX(0, k2_door);
5320 int sync_frame = kk_door * door_delay_value;
5321 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5323 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5324 &g_src_x, &g_src_y);
5329 if (!door_panel_drawn[door_index])
5331 ClearRectangle(drawto, door_rect->x, door_rect->y,
5332 door_rect->width, door_rect->height);
5334 door_panel_drawn[door_index] = TRUE;
5337 // draw opening or closing door parts
5339 if (pos->step_xoffset < 0) // door part on right side
5342 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5345 if (dst_xx + width > door_rect->width)
5346 width = door_rect->width - dst_xx;
5348 else // door part on left side
5351 dst_xx = pos->x - kk * pos->step_xoffset;
5355 src_xx = ABS(dst_xx);
5359 width = g->width - src_xx;
5361 if (width > door_rect->width)
5362 width = door_rect->width;
5364 // printf("::: k == %d [%d] \n", k, start_step);
5367 if (pos->step_yoffset < 0) // door part on bottom side
5370 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5373 if (dst_yy + height > door_rect->height)
5374 height = door_rect->height - dst_yy;
5376 else // door part on top side
5379 dst_yy = pos->y - kk * pos->step_yoffset;
5383 src_yy = ABS(dst_yy);
5387 height = g->height - src_yy;
5390 src_x = g_src_x + src_xx;
5391 src_y = g_src_y + src_yy;
5393 dst_x = door_rect->x + dst_xx;
5394 dst_y = door_rect->y + dst_yy;
5396 is_panel_and_door_has_closed =
5399 panel_has_doors[door_index] &&
5400 k >= num_move_steps_doors_only - 1);
5402 if (width >= 0 && width <= g->width &&
5403 height >= 0 && height <= g->height &&
5404 !is_panel_and_door_has_closed)
5406 if (is_panel || !pos->draw_masked)
5407 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5410 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5414 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5416 if ((part_opening && (width < 0 || height < 0)) ||
5417 (part_closing && (width >= g->width && height >= g->height)))
5418 door_part_done[nr] = TRUE;
5420 // continue door part animations, but not panel after door has closed
5421 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5422 door_part_done_all = FALSE;
5425 if (!(door_state & DOOR_NO_DELAY))
5429 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5431 current_move_delay += max_step_delay;
5433 // prevent OS (Windows) from complaining about program not responding
5437 if (door_part_done_all)
5441 if (!(door_state & DOOR_NO_DELAY))
5443 // wait for specified door action post delay
5444 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5445 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5446 else if (door_state & DOOR_ACTION_1)
5447 door_delay_value = door_1.post_delay;
5448 else if (door_state & DOOR_ACTION_2)
5449 door_delay_value = door_2.post_delay;
5451 while (!DelayReached(&door_delay, door_delay_value))
5456 if (door_state & DOOR_ACTION_1)
5457 door1 = door_state & DOOR_ACTION_1;
5458 if (door_state & DOOR_ACTION_2)
5459 door2 = door_state & DOOR_ACTION_2;
5461 // draw masked border over door area
5462 DrawMaskedBorder(REDRAW_DOOR_1);
5463 DrawMaskedBorder(REDRAW_DOOR_2);
5465 ClearAutoRepeatKeyEvents();
5467 return (door1 | door2);
5470 static boolean useSpecialEditorDoor(void)
5472 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5473 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5475 // do not draw special editor door if editor border defined or redefined
5476 if (graphic_info[graphic].bitmap != NULL || redefined)
5479 // do not draw special editor door if global border defined to be empty
5480 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5483 // do not draw special editor door if viewport definitions do not match
5487 EY + EYSIZE != VY + VYSIZE)
5493 void DrawSpecialEditorDoor(void)
5495 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5496 int top_border_width = gfx1->width;
5497 int top_border_height = gfx1->height;
5498 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5499 int ex = EX - outer_border;
5500 int ey = EY - outer_border;
5501 int vy = VY - outer_border;
5502 int exsize = EXSIZE + 2 * outer_border;
5504 if (!useSpecialEditorDoor())
5507 // draw bigger level editor toolbox window
5508 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5509 top_border_width, top_border_height, ex, ey - top_border_height);
5510 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5511 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5513 redraw_mask |= REDRAW_ALL;
5516 void UndrawSpecialEditorDoor(void)
5518 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5519 int top_border_width = gfx1->width;
5520 int top_border_height = gfx1->height;
5521 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5522 int ex = EX - outer_border;
5523 int ey = EY - outer_border;
5524 int ey_top = ey - top_border_height;
5525 int exsize = EXSIZE + 2 * outer_border;
5526 int eysize = EYSIZE + 2 * outer_border;
5528 if (!useSpecialEditorDoor())
5531 // draw normal tape recorder window
5532 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5534 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5535 ex, ey_top, top_border_width, top_border_height,
5537 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5538 ex, ey, exsize, eysize, ex, ey);
5542 // if screen background is set to "[NONE]", clear editor toolbox window
5543 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5544 ClearRectangle(drawto, ex, ey, exsize, eysize);
5547 redraw_mask |= REDRAW_ALL;
5551 // ---------- new tool button stuff -------------------------------------------
5556 struct TextPosInfo *pos;
5559 } toolbutton_info[NUM_TOOL_BUTTONS] =
5562 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5563 TOOL_CTRL_ID_YES, "yes"
5566 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5567 TOOL_CTRL_ID_NO, "no"
5570 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5571 TOOL_CTRL_ID_CONFIRM, "confirm"
5574 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5575 TOOL_CTRL_ID_PLAYER_1, "player 1"
5578 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5579 TOOL_CTRL_ID_PLAYER_2, "player 2"
5582 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5583 TOOL_CTRL_ID_PLAYER_3, "player 3"
5586 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5587 TOOL_CTRL_ID_PLAYER_4, "player 4"
5591 void CreateToolButtons(void)
5595 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5597 int graphic = toolbutton_info[i].graphic;
5598 struct GraphicInfo *gfx = &graphic_info[graphic];
5599 struct TextPosInfo *pos = toolbutton_info[i].pos;
5600 struct GadgetInfo *gi;
5601 Bitmap *deco_bitmap = None;
5602 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5603 unsigned int event_mask = GD_EVENT_RELEASED;
5606 int gd_x = gfx->src_x;
5607 int gd_y = gfx->src_y;
5608 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5609 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5614 if (global.use_envelope_request)
5616 setRequestPosition(&dx, &dy, TRUE);
5618 // check if request buttons are outside of envelope and fix, if needed
5619 if (x < 0 || x + gfx->width > request.width ||
5620 y < 0 || y + gfx->height > request.height)
5622 if (id == TOOL_CTRL_ID_YES)
5625 y = request.height - 2 * request.border_size - gfx->height;
5627 else if (id == TOOL_CTRL_ID_NO)
5629 x = request.width - 2 * request.border_size - gfx->width;
5630 y = request.height - 2 * request.border_size - gfx->height;
5632 else if (id == TOOL_CTRL_ID_CONFIRM)
5634 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5635 y = request.height - 2 * request.border_size - gfx->height;
5637 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5639 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5641 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5642 y = request.height - 2 * request.border_size - gfx->height * 2;
5644 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5645 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5650 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5652 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5654 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5655 pos->size, &deco_bitmap, &deco_x, &deco_y);
5656 deco_xpos = (gfx->width - pos->size) / 2;
5657 deco_ypos = (gfx->height - pos->size) / 2;
5660 gi = CreateGadget(GDI_CUSTOM_ID, id,
5661 GDI_IMAGE_ID, graphic,
5662 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5665 GDI_WIDTH, gfx->width,
5666 GDI_HEIGHT, gfx->height,
5667 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5668 GDI_STATE, GD_BUTTON_UNPRESSED,
5669 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5670 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5671 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5672 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5673 GDI_DECORATION_SIZE, pos->size, pos->size,
5674 GDI_DECORATION_SHIFTING, 1, 1,
5675 GDI_DIRECT_DRAW, FALSE,
5676 GDI_EVENT_MASK, event_mask,
5677 GDI_CALLBACK_ACTION, HandleToolButtons,
5681 Error(ERR_EXIT, "cannot create gadget");
5683 tool_gadget[id] = gi;
5687 void FreeToolButtons(void)
5691 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5692 FreeGadget(tool_gadget[i]);
5695 static void UnmapToolButtons(void)
5699 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5700 UnmapGadget(tool_gadget[i]);
5703 static void HandleToolButtons(struct GadgetInfo *gi)
5705 request_gadget_id = gi->custom_id;
5708 static struct Mapping_EM_to_RND_object
5711 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5712 boolean is_backside; // backside of moving element
5718 em_object_mapping_list[] =
5721 Xblank, TRUE, FALSE,
5725 Yacid_splash_eB, FALSE, FALSE,
5726 EL_ACID_SPLASH_RIGHT, -1, -1
5729 Yacid_splash_wB, FALSE, FALSE,
5730 EL_ACID_SPLASH_LEFT, -1, -1
5733 #ifdef EM_ENGINE_BAD_ROLL
5735 Xstone_force_e, FALSE, FALSE,
5736 EL_ROCK, -1, MV_BIT_RIGHT
5739 Xstone_force_w, FALSE, FALSE,
5740 EL_ROCK, -1, MV_BIT_LEFT
5743 Xnut_force_e, FALSE, FALSE,
5744 EL_NUT, -1, MV_BIT_RIGHT
5747 Xnut_force_w, FALSE, FALSE,
5748 EL_NUT, -1, MV_BIT_LEFT
5751 Xspring_force_e, FALSE, FALSE,
5752 EL_SPRING, -1, MV_BIT_RIGHT
5755 Xspring_force_w, FALSE, FALSE,
5756 EL_SPRING, -1, MV_BIT_LEFT
5759 Xemerald_force_e, FALSE, FALSE,
5760 EL_EMERALD, -1, MV_BIT_RIGHT
5763 Xemerald_force_w, FALSE, FALSE,
5764 EL_EMERALD, -1, MV_BIT_LEFT
5767 Xdiamond_force_e, FALSE, FALSE,
5768 EL_DIAMOND, -1, MV_BIT_RIGHT
5771 Xdiamond_force_w, FALSE, FALSE,
5772 EL_DIAMOND, -1, MV_BIT_LEFT
5775 Xbomb_force_e, FALSE, FALSE,
5776 EL_BOMB, -1, MV_BIT_RIGHT
5779 Xbomb_force_w, FALSE, FALSE,
5780 EL_BOMB, -1, MV_BIT_LEFT
5782 #endif // EM_ENGINE_BAD_ROLL
5785 Xstone, TRUE, FALSE,
5789 Xstone_pause, FALSE, FALSE,
5793 Xstone_fall, FALSE, FALSE,
5797 Ystone_s, FALSE, FALSE,
5798 EL_ROCK, ACTION_FALLING, -1
5801 Ystone_sB, FALSE, TRUE,
5802 EL_ROCK, ACTION_FALLING, -1
5805 Ystone_e, FALSE, FALSE,
5806 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5809 Ystone_eB, FALSE, TRUE,
5810 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5813 Ystone_w, FALSE, FALSE,
5814 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5817 Ystone_wB, FALSE, TRUE,
5818 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5825 Xnut_pause, FALSE, FALSE,
5829 Xnut_fall, FALSE, FALSE,
5833 Ynut_s, FALSE, FALSE,
5834 EL_NUT, ACTION_FALLING, -1
5837 Ynut_sB, FALSE, TRUE,
5838 EL_NUT, ACTION_FALLING, -1
5841 Ynut_e, FALSE, FALSE,
5842 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5845 Ynut_eB, FALSE, TRUE,
5846 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5849 Ynut_w, FALSE, FALSE,
5850 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5853 Ynut_wB, FALSE, TRUE,
5854 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5857 Xbug_n, TRUE, FALSE,
5861 Xbug_e, TRUE, FALSE,
5862 EL_BUG_RIGHT, -1, -1
5865 Xbug_s, TRUE, FALSE,
5869 Xbug_w, TRUE, FALSE,
5873 Xbug_gon, FALSE, FALSE,
5877 Xbug_goe, FALSE, FALSE,
5878 EL_BUG_RIGHT, -1, -1
5881 Xbug_gos, FALSE, FALSE,
5885 Xbug_gow, FALSE, FALSE,
5889 Ybug_n, FALSE, FALSE,
5890 EL_BUG, ACTION_MOVING, MV_BIT_UP
5893 Ybug_nB, FALSE, TRUE,
5894 EL_BUG, ACTION_MOVING, MV_BIT_UP
5897 Ybug_e, FALSE, FALSE,
5898 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5901 Ybug_eB, FALSE, TRUE,
5902 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5905 Ybug_s, FALSE, FALSE,
5906 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5909 Ybug_sB, FALSE, TRUE,
5910 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5913 Ybug_w, FALSE, FALSE,
5914 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5917 Ybug_wB, FALSE, TRUE,
5918 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5921 Ybug_w_n, FALSE, FALSE,
5922 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5925 Ybug_n_e, FALSE, FALSE,
5926 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5929 Ybug_e_s, FALSE, FALSE,
5930 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5933 Ybug_s_w, FALSE, FALSE,
5934 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5937 Ybug_e_n, FALSE, FALSE,
5938 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5941 Ybug_s_e, FALSE, FALSE,
5942 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5945 Ybug_w_s, FALSE, FALSE,
5946 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5949 Ybug_n_w, FALSE, FALSE,
5950 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5953 Ybug_stone, FALSE, FALSE,
5954 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5957 Ybug_spring, FALSE, FALSE,
5958 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5961 Xtank_n, TRUE, FALSE,
5962 EL_SPACESHIP_UP, -1, -1
5965 Xtank_e, TRUE, FALSE,
5966 EL_SPACESHIP_RIGHT, -1, -1
5969 Xtank_s, TRUE, FALSE,
5970 EL_SPACESHIP_DOWN, -1, -1
5973 Xtank_w, TRUE, FALSE,
5974 EL_SPACESHIP_LEFT, -1, -1
5977 Xtank_gon, FALSE, FALSE,
5978 EL_SPACESHIP_UP, -1, -1
5981 Xtank_goe, FALSE, FALSE,
5982 EL_SPACESHIP_RIGHT, -1, -1
5985 Xtank_gos, FALSE, FALSE,
5986 EL_SPACESHIP_DOWN, -1, -1
5989 Xtank_gow, FALSE, FALSE,
5990 EL_SPACESHIP_LEFT, -1, -1
5993 Ytank_n, FALSE, FALSE,
5994 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5997 Ytank_nB, FALSE, TRUE,
5998 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
6001 Ytank_e, FALSE, FALSE,
6002 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6005 Ytank_eB, FALSE, TRUE,
6006 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6009 Ytank_s, FALSE, FALSE,
6010 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6013 Ytank_sB, FALSE, TRUE,
6014 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6017 Ytank_w, FALSE, FALSE,
6018 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6021 Ytank_wB, FALSE, TRUE,
6022 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6025 Ytank_w_n, FALSE, FALSE,
6026 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6029 Ytank_n_e, FALSE, FALSE,
6030 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6033 Ytank_e_s, FALSE, FALSE,
6034 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6037 Ytank_s_w, FALSE, FALSE,
6038 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6041 Ytank_e_n, FALSE, FALSE,
6042 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6045 Ytank_s_e, FALSE, FALSE,
6046 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6049 Ytank_w_s, FALSE, FALSE,
6050 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6053 Ytank_n_w, FALSE, FALSE,
6054 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6057 Ytank_stone, FALSE, FALSE,
6058 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6061 Ytank_spring, FALSE, FALSE,
6062 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6065 Xandroid, TRUE, FALSE,
6066 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6069 Xandroid_1_n, FALSE, FALSE,
6070 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6073 Xandroid_2_n, FALSE, FALSE,
6074 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6077 Xandroid_1_e, FALSE, FALSE,
6078 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6081 Xandroid_2_e, FALSE, FALSE,
6082 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6085 Xandroid_1_w, FALSE, FALSE,
6086 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6089 Xandroid_2_w, FALSE, FALSE,
6090 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6093 Xandroid_1_s, FALSE, FALSE,
6094 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6097 Xandroid_2_s, FALSE, FALSE,
6098 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6101 Yandroid_n, FALSE, FALSE,
6102 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6105 Yandroid_nB, FALSE, TRUE,
6106 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6109 Yandroid_ne, FALSE, FALSE,
6110 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6113 Yandroid_neB, FALSE, TRUE,
6114 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6117 Yandroid_e, FALSE, FALSE,
6118 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6121 Yandroid_eB, FALSE, TRUE,
6122 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6125 Yandroid_se, FALSE, FALSE,
6126 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6129 Yandroid_seB, FALSE, TRUE,
6130 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6133 Yandroid_s, FALSE, FALSE,
6134 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6137 Yandroid_sB, FALSE, TRUE,
6138 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6141 Yandroid_sw, FALSE, FALSE,
6142 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6145 Yandroid_swB, FALSE, TRUE,
6146 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6149 Yandroid_w, FALSE, FALSE,
6150 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6153 Yandroid_wB, FALSE, TRUE,
6154 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6157 Yandroid_nw, FALSE, FALSE,
6158 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6161 Yandroid_nwB, FALSE, TRUE,
6162 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6165 Xspring, TRUE, FALSE,
6169 Xspring_pause, FALSE, FALSE,
6173 Xspring_e, FALSE, FALSE,
6177 Xspring_w, FALSE, FALSE,
6181 Xspring_fall, FALSE, FALSE,
6185 Yspring_s, FALSE, FALSE,
6186 EL_SPRING, ACTION_FALLING, -1
6189 Yspring_sB, FALSE, TRUE,
6190 EL_SPRING, ACTION_FALLING, -1
6193 Yspring_e, FALSE, FALSE,
6194 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6197 Yspring_eB, FALSE, TRUE,
6198 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6201 Yspring_w, FALSE, FALSE,
6202 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6205 Yspring_wB, FALSE, TRUE,
6206 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6209 Yspring_kill_e, FALSE, FALSE,
6210 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6213 Yspring_kill_eB, FALSE, TRUE,
6214 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6217 Yspring_kill_w, FALSE, FALSE,
6218 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6221 Yspring_kill_wB, FALSE, TRUE,
6222 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6225 Xeater_n, TRUE, FALSE,
6226 EL_YAMYAM_UP, -1, -1
6229 Xeater_e, TRUE, FALSE,
6230 EL_YAMYAM_RIGHT, -1, -1
6233 Xeater_w, TRUE, FALSE,
6234 EL_YAMYAM_LEFT, -1, -1
6237 Xeater_s, TRUE, FALSE,
6238 EL_YAMYAM_DOWN, -1, -1
6241 Yeater_n, FALSE, FALSE,
6242 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6245 Yeater_nB, FALSE, TRUE,
6246 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6249 Yeater_e, FALSE, FALSE,
6250 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6253 Yeater_eB, FALSE, TRUE,
6254 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6257 Yeater_s, FALSE, FALSE,
6258 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6261 Yeater_sB, FALSE, TRUE,
6262 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6265 Yeater_w, FALSE, FALSE,
6266 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6269 Yeater_wB, FALSE, TRUE,
6270 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6273 Yeater_stone, FALSE, FALSE,
6274 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6277 Yeater_spring, FALSE, FALSE,
6278 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6281 Xalien, TRUE, FALSE,
6285 Xalien_pause, FALSE, FALSE,
6289 Yalien_n, FALSE, FALSE,
6290 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6293 Yalien_nB, FALSE, TRUE,
6294 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6297 Yalien_e, FALSE, FALSE,
6298 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6301 Yalien_eB, FALSE, TRUE,
6302 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6305 Yalien_s, FALSE, FALSE,
6306 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6309 Yalien_sB, FALSE, TRUE,
6310 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6313 Yalien_w, FALSE, FALSE,
6314 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6317 Yalien_wB, FALSE, TRUE,
6318 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6321 Yalien_stone, FALSE, FALSE,
6322 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6325 Yalien_spring, FALSE, FALSE,
6326 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6329 Xemerald, TRUE, FALSE,
6333 Xemerald_pause, FALSE, FALSE,
6337 Xemerald_fall, FALSE, FALSE,
6341 Xemerald_shine, FALSE, FALSE,
6342 EL_EMERALD, ACTION_TWINKLING, -1
6345 Yemerald_s, FALSE, FALSE,
6346 EL_EMERALD, ACTION_FALLING, -1
6349 Yemerald_sB, FALSE, TRUE,
6350 EL_EMERALD, ACTION_FALLING, -1
6353 Yemerald_e, FALSE, FALSE,
6354 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6357 Yemerald_eB, FALSE, TRUE,
6358 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6361 Yemerald_w, FALSE, FALSE,
6362 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6365 Yemerald_wB, FALSE, TRUE,
6366 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6369 Yemerald_eat, FALSE, FALSE,
6370 EL_EMERALD, ACTION_COLLECTING, -1
6373 Yemerald_stone, FALSE, FALSE,
6374 EL_NUT, ACTION_BREAKING, -1
6377 Xdiamond, TRUE, FALSE,
6381 Xdiamond_pause, FALSE, FALSE,
6385 Xdiamond_fall, FALSE, FALSE,
6389 Xdiamond_shine, FALSE, FALSE,
6390 EL_DIAMOND, ACTION_TWINKLING, -1
6393 Ydiamond_s, FALSE, FALSE,
6394 EL_DIAMOND, ACTION_FALLING, -1
6397 Ydiamond_sB, FALSE, TRUE,
6398 EL_DIAMOND, ACTION_FALLING, -1
6401 Ydiamond_e, FALSE, FALSE,
6402 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6405 Ydiamond_eB, FALSE, TRUE,
6406 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6409 Ydiamond_w, FALSE, FALSE,
6410 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6413 Ydiamond_wB, FALSE, TRUE,
6414 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6417 Ydiamond_eat, FALSE, FALSE,
6418 EL_DIAMOND, ACTION_COLLECTING, -1
6421 Ydiamond_stone, FALSE, FALSE,
6422 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6425 Xdrip_fall, TRUE, FALSE,
6426 EL_AMOEBA_DROP, -1, -1
6429 Xdrip_stretch, FALSE, FALSE,
6430 EL_AMOEBA_DROP, ACTION_FALLING, -1
6433 Xdrip_stretchB, FALSE, TRUE,
6434 EL_AMOEBA_DROP, ACTION_FALLING, -1
6437 Xdrip_eat, FALSE, FALSE,
6438 EL_AMOEBA_DROP, ACTION_GROWING, -1
6441 Ydrip_s1, FALSE, FALSE,
6442 EL_AMOEBA_DROP, ACTION_FALLING, -1
6445 Ydrip_s1B, FALSE, TRUE,
6446 EL_AMOEBA_DROP, ACTION_FALLING, -1
6449 Ydrip_s2, FALSE, FALSE,
6450 EL_AMOEBA_DROP, ACTION_FALLING, -1
6453 Ydrip_s2B, FALSE, TRUE,
6454 EL_AMOEBA_DROP, ACTION_FALLING, -1
6461 Xbomb_pause, FALSE, FALSE,
6465 Xbomb_fall, FALSE, FALSE,
6469 Ybomb_s, FALSE, FALSE,
6470 EL_BOMB, ACTION_FALLING, -1
6473 Ybomb_sB, FALSE, TRUE,
6474 EL_BOMB, ACTION_FALLING, -1
6477 Ybomb_e, FALSE, FALSE,
6478 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6481 Ybomb_eB, FALSE, TRUE,
6482 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6485 Ybomb_w, FALSE, FALSE,
6486 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6489 Ybomb_wB, FALSE, TRUE,
6490 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6493 Ybomb_eat, FALSE, FALSE,
6494 EL_BOMB, ACTION_ACTIVATING, -1
6497 Xballoon, TRUE, FALSE,
6501 Yballoon_n, FALSE, FALSE,
6502 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6505 Yballoon_nB, FALSE, TRUE,
6506 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6509 Yballoon_e, FALSE, FALSE,
6510 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6513 Yballoon_eB, FALSE, TRUE,
6514 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6517 Yballoon_s, FALSE, FALSE,
6518 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6521 Yballoon_sB, FALSE, TRUE,
6522 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6525 Yballoon_w, FALSE, FALSE,
6526 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6529 Yballoon_wB, FALSE, TRUE,
6530 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6533 Xgrass, TRUE, FALSE,
6534 EL_EMC_GRASS, -1, -1
6537 Ygrass_nB, FALSE, FALSE,
6538 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6541 Ygrass_eB, FALSE, FALSE,
6542 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6545 Ygrass_sB, FALSE, FALSE,
6546 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6549 Ygrass_wB, FALSE, FALSE,
6550 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6557 Ydirt_nB, FALSE, FALSE,
6558 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6561 Ydirt_eB, FALSE, FALSE,
6562 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6565 Ydirt_sB, FALSE, FALSE,
6566 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6569 Ydirt_wB, FALSE, FALSE,
6570 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6573 Xacid_ne, TRUE, FALSE,
6574 EL_ACID_POOL_TOPRIGHT, -1, -1
6577 Xacid_se, TRUE, FALSE,
6578 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6581 Xacid_s, TRUE, FALSE,
6582 EL_ACID_POOL_BOTTOM, -1, -1
6585 Xacid_sw, TRUE, FALSE,
6586 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6589 Xacid_nw, TRUE, FALSE,
6590 EL_ACID_POOL_TOPLEFT, -1, -1
6593 Xacid_1, TRUE, FALSE,
6597 Xacid_2, FALSE, FALSE,
6601 Xacid_3, FALSE, FALSE,
6605 Xacid_4, FALSE, FALSE,
6609 Xacid_5, FALSE, FALSE,
6613 Xacid_6, FALSE, FALSE,
6617 Xacid_7, FALSE, FALSE,
6621 Xacid_8, FALSE, FALSE,
6625 Xball_1, TRUE, FALSE,
6626 EL_EMC_MAGIC_BALL, -1, -1
6629 Xball_1B, FALSE, FALSE,
6630 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6633 Xball_2, FALSE, FALSE,
6634 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6637 Xball_2B, FALSE, FALSE,
6638 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6641 Yball_eat, FALSE, FALSE,
6642 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6645 Ykey_1_eat, FALSE, FALSE,
6646 EL_EM_KEY_1, ACTION_COLLECTING, -1
6649 Ykey_2_eat, FALSE, FALSE,
6650 EL_EM_KEY_2, ACTION_COLLECTING, -1
6653 Ykey_3_eat, FALSE, FALSE,
6654 EL_EM_KEY_3, ACTION_COLLECTING, -1
6657 Ykey_4_eat, FALSE, FALSE,
6658 EL_EM_KEY_4, ACTION_COLLECTING, -1
6661 Ykey_5_eat, FALSE, FALSE,
6662 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6665 Ykey_6_eat, FALSE, FALSE,
6666 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6669 Ykey_7_eat, FALSE, FALSE,
6670 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6673 Ykey_8_eat, FALSE, FALSE,
6674 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6677 Ylenses_eat, FALSE, FALSE,
6678 EL_EMC_LENSES, ACTION_COLLECTING, -1
6681 Ymagnify_eat, FALSE, FALSE,
6682 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6685 Ygrass_eat, FALSE, FALSE,
6686 EL_EMC_GRASS, ACTION_SNAPPING, -1
6689 Ydirt_eat, FALSE, FALSE,
6690 EL_SAND, ACTION_SNAPPING, -1
6693 Xgrow_ns, TRUE, FALSE,
6694 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6697 Ygrow_ns_eat, FALSE, FALSE,
6698 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6701 Xgrow_ew, TRUE, FALSE,
6702 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6705 Ygrow_ew_eat, FALSE, FALSE,
6706 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6709 Xwonderwall, TRUE, FALSE,
6710 EL_MAGIC_WALL, -1, -1
6713 XwonderwallB, FALSE, FALSE,
6714 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6717 Xamoeba_1, TRUE, FALSE,
6718 EL_AMOEBA_DRY, ACTION_OTHER, -1
6721 Xamoeba_2, FALSE, FALSE,
6722 EL_AMOEBA_DRY, ACTION_OTHER, -1
6725 Xamoeba_3, FALSE, FALSE,
6726 EL_AMOEBA_DRY, ACTION_OTHER, -1
6729 Xamoeba_4, FALSE, FALSE,
6730 EL_AMOEBA_DRY, ACTION_OTHER, -1
6733 Xamoeba_5, TRUE, FALSE,
6734 EL_AMOEBA_WET, ACTION_OTHER, -1
6737 Xamoeba_6, FALSE, FALSE,
6738 EL_AMOEBA_WET, ACTION_OTHER, -1
6741 Xamoeba_7, FALSE, FALSE,
6742 EL_AMOEBA_WET, ACTION_OTHER, -1
6745 Xamoeba_8, FALSE, FALSE,
6746 EL_AMOEBA_WET, ACTION_OTHER, -1
6749 Xdoor_1, TRUE, FALSE,
6750 EL_EM_GATE_1, -1, -1
6753 Xdoor_2, TRUE, FALSE,
6754 EL_EM_GATE_2, -1, -1
6757 Xdoor_3, TRUE, FALSE,
6758 EL_EM_GATE_3, -1, -1
6761 Xdoor_4, TRUE, FALSE,
6762 EL_EM_GATE_4, -1, -1
6765 Xdoor_5, TRUE, FALSE,
6766 EL_EMC_GATE_5, -1, -1
6769 Xdoor_6, TRUE, FALSE,
6770 EL_EMC_GATE_6, -1, -1
6773 Xdoor_7, TRUE, FALSE,
6774 EL_EMC_GATE_7, -1, -1
6777 Xdoor_8, TRUE, FALSE,
6778 EL_EMC_GATE_8, -1, -1
6781 Xkey_1, TRUE, FALSE,
6785 Xkey_2, TRUE, FALSE,
6789 Xkey_3, TRUE, FALSE,
6793 Xkey_4, TRUE, FALSE,
6797 Xkey_5, TRUE, FALSE,
6798 EL_EMC_KEY_5, -1, -1
6801 Xkey_6, TRUE, FALSE,
6802 EL_EMC_KEY_6, -1, -1
6805 Xkey_7, TRUE, FALSE,
6806 EL_EMC_KEY_7, -1, -1
6809 Xkey_8, TRUE, FALSE,
6810 EL_EMC_KEY_8, -1, -1
6813 Xwind_n, TRUE, FALSE,
6814 EL_BALLOON_SWITCH_UP, -1, -1
6817 Xwind_e, TRUE, FALSE,
6818 EL_BALLOON_SWITCH_RIGHT, -1, -1
6821 Xwind_s, TRUE, FALSE,
6822 EL_BALLOON_SWITCH_DOWN, -1, -1
6825 Xwind_w, TRUE, FALSE,
6826 EL_BALLOON_SWITCH_LEFT, -1, -1
6829 Xwind_nesw, TRUE, FALSE,
6830 EL_BALLOON_SWITCH_ANY, -1, -1
6833 Xwind_stop, TRUE, FALSE,
6834 EL_BALLOON_SWITCH_NONE, -1, -1
6838 EL_EM_EXIT_CLOSED, -1, -1
6841 Xexit_1, TRUE, FALSE,
6842 EL_EM_EXIT_OPEN, -1, -1
6845 Xexit_2, FALSE, FALSE,
6846 EL_EM_EXIT_OPEN, -1, -1
6849 Xexit_3, FALSE, FALSE,
6850 EL_EM_EXIT_OPEN, -1, -1
6853 Xdynamite, TRUE, FALSE,
6854 EL_EM_DYNAMITE, -1, -1
6857 Ydynamite_eat, FALSE, FALSE,
6858 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6861 Xdynamite_1, TRUE, FALSE,
6862 EL_EM_DYNAMITE_ACTIVE, -1, -1
6865 Xdynamite_2, FALSE, FALSE,
6866 EL_EM_DYNAMITE_ACTIVE, -1, -1
6869 Xdynamite_3, FALSE, FALSE,
6870 EL_EM_DYNAMITE_ACTIVE, -1, -1
6873 Xdynamite_4, FALSE, FALSE,
6874 EL_EM_DYNAMITE_ACTIVE, -1, -1
6877 Xbumper, TRUE, FALSE,
6878 EL_EMC_SPRING_BUMPER, -1, -1
6881 XbumperB, FALSE, FALSE,
6882 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6885 Xwheel, TRUE, FALSE,
6886 EL_ROBOT_WHEEL, -1, -1
6889 XwheelB, FALSE, FALSE,
6890 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6893 Xswitch, TRUE, FALSE,
6894 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6897 XswitchB, FALSE, FALSE,
6898 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6902 EL_QUICKSAND_EMPTY, -1, -1
6905 Xsand_stone, TRUE, FALSE,
6906 EL_QUICKSAND_FULL, -1, -1
6909 Xsand_stonein_1, FALSE, TRUE,
6910 EL_ROCK, ACTION_FILLING, -1
6913 Xsand_stonein_2, FALSE, TRUE,
6914 EL_ROCK, ACTION_FILLING, -1
6917 Xsand_stonein_3, FALSE, TRUE,
6918 EL_ROCK, ACTION_FILLING, -1
6921 Xsand_stonein_4, FALSE, TRUE,
6922 EL_ROCK, ACTION_FILLING, -1
6925 Xsand_stonesand_1, FALSE, FALSE,
6926 EL_QUICKSAND_EMPTYING, -1, -1
6929 Xsand_stonesand_2, FALSE, FALSE,
6930 EL_QUICKSAND_EMPTYING, -1, -1
6933 Xsand_stonesand_3, FALSE, FALSE,
6934 EL_QUICKSAND_EMPTYING, -1, -1
6937 Xsand_stonesand_4, FALSE, FALSE,
6938 EL_QUICKSAND_EMPTYING, -1, -1
6941 Xsand_stonesand_quickout_1, FALSE, FALSE,
6942 EL_QUICKSAND_EMPTYING, -1, -1
6945 Xsand_stonesand_quickout_2, FALSE, FALSE,
6946 EL_QUICKSAND_EMPTYING, -1, -1
6949 Xsand_stoneout_1, FALSE, FALSE,
6950 EL_ROCK, ACTION_EMPTYING, -1
6953 Xsand_stoneout_2, FALSE, FALSE,
6954 EL_ROCK, ACTION_EMPTYING, -1
6957 Xsand_sandstone_1, FALSE, FALSE,
6958 EL_QUICKSAND_FILLING, -1, -1
6961 Xsand_sandstone_2, FALSE, FALSE,
6962 EL_QUICKSAND_FILLING, -1, -1
6965 Xsand_sandstone_3, FALSE, FALSE,
6966 EL_QUICKSAND_FILLING, -1, -1
6969 Xsand_sandstone_4, FALSE, FALSE,
6970 EL_QUICKSAND_FILLING, -1, -1
6973 Xplant, TRUE, FALSE,
6974 EL_EMC_PLANT, -1, -1
6977 Yplant, FALSE, FALSE,
6978 EL_EMC_PLANT, -1, -1
6981 Xlenses, TRUE, FALSE,
6982 EL_EMC_LENSES, -1, -1
6985 Xmagnify, TRUE, FALSE,
6986 EL_EMC_MAGNIFIER, -1, -1
6989 Xdripper, TRUE, FALSE,
6990 EL_EMC_DRIPPER, -1, -1
6993 XdripperB, FALSE, FALSE,
6994 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6997 Xfake_blank, TRUE, FALSE,
6998 EL_INVISIBLE_WALL, -1, -1
7001 Xfake_blankB, FALSE, FALSE,
7002 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7005 Xfake_grass, TRUE, FALSE,
7006 EL_EMC_FAKE_GRASS, -1, -1
7009 Xfake_grassB, FALSE, FALSE,
7010 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7013 Xfake_door_1, TRUE, FALSE,
7014 EL_EM_GATE_1_GRAY, -1, -1
7017 Xfake_door_2, TRUE, FALSE,
7018 EL_EM_GATE_2_GRAY, -1, -1
7021 Xfake_door_3, TRUE, FALSE,
7022 EL_EM_GATE_3_GRAY, -1, -1
7025 Xfake_door_4, TRUE, FALSE,
7026 EL_EM_GATE_4_GRAY, -1, -1
7029 Xfake_door_5, TRUE, FALSE,
7030 EL_EMC_GATE_5_GRAY, -1, -1
7033 Xfake_door_6, TRUE, FALSE,
7034 EL_EMC_GATE_6_GRAY, -1, -1
7037 Xfake_door_7, TRUE, FALSE,
7038 EL_EMC_GATE_7_GRAY, -1, -1
7041 Xfake_door_8, TRUE, FALSE,
7042 EL_EMC_GATE_8_GRAY, -1, -1
7045 Xfake_acid_1, TRUE, FALSE,
7046 EL_EMC_FAKE_ACID, -1, -1
7049 Xfake_acid_2, FALSE, FALSE,
7050 EL_EMC_FAKE_ACID, -1, -1
7053 Xfake_acid_3, FALSE, FALSE,
7054 EL_EMC_FAKE_ACID, -1, -1
7057 Xfake_acid_4, FALSE, FALSE,
7058 EL_EMC_FAKE_ACID, -1, -1
7061 Xfake_acid_5, FALSE, FALSE,
7062 EL_EMC_FAKE_ACID, -1, -1
7065 Xfake_acid_6, FALSE, FALSE,
7066 EL_EMC_FAKE_ACID, -1, -1
7069 Xfake_acid_7, FALSE, FALSE,
7070 EL_EMC_FAKE_ACID, -1, -1
7073 Xfake_acid_8, FALSE, FALSE,
7074 EL_EMC_FAKE_ACID, -1, -1
7077 Xsteel_1, TRUE, FALSE,
7078 EL_STEELWALL, -1, -1
7081 Xsteel_2, TRUE, FALSE,
7082 EL_EMC_STEELWALL_2, -1, -1
7085 Xsteel_3, TRUE, FALSE,
7086 EL_EMC_STEELWALL_3, -1, -1
7089 Xsteel_4, TRUE, FALSE,
7090 EL_EMC_STEELWALL_4, -1, -1
7093 Xwall_1, TRUE, FALSE,
7097 Xwall_2, TRUE, FALSE,
7098 EL_EMC_WALL_14, -1, -1
7101 Xwall_3, TRUE, FALSE,
7102 EL_EMC_WALL_15, -1, -1
7105 Xwall_4, TRUE, FALSE,
7106 EL_EMC_WALL_16, -1, -1
7109 Xround_wall_1, TRUE, FALSE,
7110 EL_WALL_SLIPPERY, -1, -1
7113 Xround_wall_2, TRUE, FALSE,
7114 EL_EMC_WALL_SLIPPERY_2, -1, -1
7117 Xround_wall_3, TRUE, FALSE,
7118 EL_EMC_WALL_SLIPPERY_3, -1, -1
7121 Xround_wall_4, TRUE, FALSE,
7122 EL_EMC_WALL_SLIPPERY_4, -1, -1
7125 Xdecor_1, TRUE, FALSE,
7126 EL_EMC_WALL_8, -1, -1
7129 Xdecor_2, TRUE, FALSE,
7130 EL_EMC_WALL_6, -1, -1
7133 Xdecor_3, TRUE, FALSE,
7134 EL_EMC_WALL_4, -1, -1
7137 Xdecor_4, TRUE, FALSE,
7138 EL_EMC_WALL_7, -1, -1
7141 Xdecor_5, TRUE, FALSE,
7142 EL_EMC_WALL_5, -1, -1
7145 Xdecor_6, TRUE, FALSE,
7146 EL_EMC_WALL_9, -1, -1
7149 Xdecor_7, TRUE, FALSE,
7150 EL_EMC_WALL_10, -1, -1
7153 Xdecor_8, TRUE, FALSE,
7154 EL_EMC_WALL_1, -1, -1
7157 Xdecor_9, TRUE, FALSE,
7158 EL_EMC_WALL_2, -1, -1
7161 Xdecor_10, TRUE, FALSE,
7162 EL_EMC_WALL_3, -1, -1
7165 Xdecor_11, TRUE, FALSE,
7166 EL_EMC_WALL_11, -1, -1
7169 Xdecor_12, TRUE, FALSE,
7170 EL_EMC_WALL_12, -1, -1
7173 Xalpha_0, TRUE, FALSE,
7174 EL_CHAR('0'), -1, -1
7177 Xalpha_1, TRUE, FALSE,
7178 EL_CHAR('1'), -1, -1
7181 Xalpha_2, TRUE, FALSE,
7182 EL_CHAR('2'), -1, -1
7185 Xalpha_3, TRUE, FALSE,
7186 EL_CHAR('3'), -1, -1
7189 Xalpha_4, TRUE, FALSE,
7190 EL_CHAR('4'), -1, -1
7193 Xalpha_5, TRUE, FALSE,
7194 EL_CHAR('5'), -1, -1
7197 Xalpha_6, TRUE, FALSE,
7198 EL_CHAR('6'), -1, -1
7201 Xalpha_7, TRUE, FALSE,
7202 EL_CHAR('7'), -1, -1
7205 Xalpha_8, TRUE, FALSE,
7206 EL_CHAR('8'), -1, -1
7209 Xalpha_9, TRUE, FALSE,
7210 EL_CHAR('9'), -1, -1
7213 Xalpha_excla, TRUE, FALSE,
7214 EL_CHAR('!'), -1, -1
7217 Xalpha_quote, TRUE, FALSE,
7218 EL_CHAR('"'), -1, -1
7221 Xalpha_comma, TRUE, FALSE,
7222 EL_CHAR(','), -1, -1
7225 Xalpha_minus, TRUE, FALSE,
7226 EL_CHAR('-'), -1, -1
7229 Xalpha_perio, TRUE, FALSE,
7230 EL_CHAR('.'), -1, -1
7233 Xalpha_colon, TRUE, FALSE,
7234 EL_CHAR(':'), -1, -1
7237 Xalpha_quest, TRUE, FALSE,
7238 EL_CHAR('?'), -1, -1
7241 Xalpha_a, TRUE, FALSE,
7242 EL_CHAR('A'), -1, -1
7245 Xalpha_b, TRUE, FALSE,
7246 EL_CHAR('B'), -1, -1
7249 Xalpha_c, TRUE, FALSE,
7250 EL_CHAR('C'), -1, -1
7253 Xalpha_d, TRUE, FALSE,
7254 EL_CHAR('D'), -1, -1
7257 Xalpha_e, TRUE, FALSE,
7258 EL_CHAR('E'), -1, -1
7261 Xalpha_f, TRUE, FALSE,
7262 EL_CHAR('F'), -1, -1
7265 Xalpha_g, TRUE, FALSE,
7266 EL_CHAR('G'), -1, -1
7269 Xalpha_h, TRUE, FALSE,
7270 EL_CHAR('H'), -1, -1
7273 Xalpha_i, TRUE, FALSE,
7274 EL_CHAR('I'), -1, -1
7277 Xalpha_j, TRUE, FALSE,
7278 EL_CHAR('J'), -1, -1
7281 Xalpha_k, TRUE, FALSE,
7282 EL_CHAR('K'), -1, -1
7285 Xalpha_l, TRUE, FALSE,
7286 EL_CHAR('L'), -1, -1
7289 Xalpha_m, TRUE, FALSE,
7290 EL_CHAR('M'), -1, -1
7293 Xalpha_n, TRUE, FALSE,
7294 EL_CHAR('N'), -1, -1
7297 Xalpha_o, TRUE, FALSE,
7298 EL_CHAR('O'), -1, -1
7301 Xalpha_p, TRUE, FALSE,
7302 EL_CHAR('P'), -1, -1
7305 Xalpha_q, TRUE, FALSE,
7306 EL_CHAR('Q'), -1, -1
7309 Xalpha_r, TRUE, FALSE,
7310 EL_CHAR('R'), -1, -1
7313 Xalpha_s, TRUE, FALSE,
7314 EL_CHAR('S'), -1, -1
7317 Xalpha_t, TRUE, FALSE,
7318 EL_CHAR('T'), -1, -1
7321 Xalpha_u, TRUE, FALSE,
7322 EL_CHAR('U'), -1, -1
7325 Xalpha_v, TRUE, FALSE,
7326 EL_CHAR('V'), -1, -1
7329 Xalpha_w, TRUE, FALSE,
7330 EL_CHAR('W'), -1, -1
7333 Xalpha_x, TRUE, FALSE,
7334 EL_CHAR('X'), -1, -1
7337 Xalpha_y, TRUE, FALSE,
7338 EL_CHAR('Y'), -1, -1
7341 Xalpha_z, TRUE, FALSE,
7342 EL_CHAR('Z'), -1, -1
7345 Xalpha_arrow_e, TRUE, FALSE,
7346 EL_CHAR('>'), -1, -1
7349 Xalpha_arrow_w, TRUE, FALSE,
7350 EL_CHAR('<'), -1, -1
7353 Xalpha_copyr, TRUE, FALSE,
7354 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7358 Xboom_bug, FALSE, FALSE,
7359 EL_BUG, ACTION_EXPLODING, -1
7362 Xboom_bomb, FALSE, FALSE,
7363 EL_BOMB, ACTION_EXPLODING, -1
7366 Xboom_android, FALSE, FALSE,
7367 EL_EMC_ANDROID, ACTION_OTHER, -1
7370 Xboom_1, FALSE, FALSE,
7371 EL_DEFAULT, ACTION_EXPLODING, -1
7374 Xboom_2, FALSE, FALSE,
7375 EL_DEFAULT, ACTION_EXPLODING, -1
7378 Znormal, FALSE, FALSE,
7382 Zdynamite, FALSE, FALSE,
7386 Zplayer, FALSE, FALSE,
7390 ZBORDER, FALSE, FALSE,
7400 static struct Mapping_EM_to_RND_player
7409 em_player_mapping_list[] =
7413 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7417 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7421 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7425 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7429 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7433 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7437 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7441 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7445 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7449 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7453 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7457 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7461 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7465 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7469 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7473 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7477 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7481 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7485 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7489 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7493 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7497 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7501 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7505 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7509 EL_PLAYER_1, ACTION_DEFAULT, -1,
7513 EL_PLAYER_2, ACTION_DEFAULT, -1,
7517 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7521 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7525 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7529 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7533 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7537 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7541 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7545 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7549 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7553 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7557 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7561 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7565 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7569 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7573 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7577 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7581 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7585 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7589 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7593 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7597 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7601 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7605 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7609 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7613 EL_PLAYER_3, ACTION_DEFAULT, -1,
7617 EL_PLAYER_4, ACTION_DEFAULT, -1,
7626 int map_element_RND_to_EM(int element_rnd)
7628 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7629 static boolean mapping_initialized = FALSE;
7631 if (!mapping_initialized)
7635 // return "Xalpha_quest" for all undefined elements in mapping array
7636 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7637 mapping_RND_to_EM[i] = Xalpha_quest;
7639 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7640 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7641 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7642 em_object_mapping_list[i].element_em;
7644 mapping_initialized = TRUE;
7647 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7648 return mapping_RND_to_EM[element_rnd];
7650 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7655 int map_element_EM_to_RND(int element_em)
7657 static unsigned short mapping_EM_to_RND[TILE_MAX];
7658 static boolean mapping_initialized = FALSE;
7660 if (!mapping_initialized)
7664 // return "EL_UNKNOWN" for all undefined elements in mapping array
7665 for (i = 0; i < TILE_MAX; i++)
7666 mapping_EM_to_RND[i] = EL_UNKNOWN;
7668 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7669 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7670 em_object_mapping_list[i].element_rnd;
7672 mapping_initialized = TRUE;
7675 if (element_em >= 0 && element_em < TILE_MAX)
7676 return mapping_EM_to_RND[element_em];
7678 Error(ERR_WARN, "invalid EM level element %d", element_em);
7683 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7685 struct LevelInfo_EM *level_em = level->native_em_level;
7686 struct LEVEL *lev = level_em->lev;
7689 for (i = 0; i < TILE_MAX; i++)
7690 lev->android_array[i] = Xblank;
7692 for (i = 0; i < level->num_android_clone_elements; i++)
7694 int element_rnd = level->android_clone_element[i];
7695 int element_em = map_element_RND_to_EM(element_rnd);
7697 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7698 if (em_object_mapping_list[j].element_rnd == element_rnd)
7699 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7703 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7705 struct LevelInfo_EM *level_em = level->native_em_level;
7706 struct LEVEL *lev = level_em->lev;
7709 level->num_android_clone_elements = 0;
7711 for (i = 0; i < TILE_MAX; i++)
7713 int element_em = lev->android_array[i];
7715 boolean element_found = FALSE;
7717 if (element_em == Xblank)
7720 element_rnd = map_element_EM_to_RND(element_em);
7722 for (j = 0; j < level->num_android_clone_elements; j++)
7723 if (level->android_clone_element[j] == element_rnd)
7724 element_found = TRUE;
7728 level->android_clone_element[level->num_android_clone_elements++] =
7731 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7736 if (level->num_android_clone_elements == 0)
7738 level->num_android_clone_elements = 1;
7739 level->android_clone_element[0] = EL_EMPTY;
7743 int map_direction_RND_to_EM(int direction)
7745 return (direction == MV_UP ? 0 :
7746 direction == MV_RIGHT ? 1 :
7747 direction == MV_DOWN ? 2 :
7748 direction == MV_LEFT ? 3 :
7752 int map_direction_EM_to_RND(int direction)
7754 return (direction == 0 ? MV_UP :
7755 direction == 1 ? MV_RIGHT :
7756 direction == 2 ? MV_DOWN :
7757 direction == 3 ? MV_LEFT :
7761 int map_element_RND_to_SP(int element_rnd)
7763 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7765 if (element_rnd >= EL_SP_START &&
7766 element_rnd <= EL_SP_END)
7767 element_sp = element_rnd - EL_SP_START;
7768 else if (element_rnd == EL_EMPTY_SPACE)
7770 else if (element_rnd == EL_INVISIBLE_WALL)
7776 int map_element_SP_to_RND(int element_sp)
7778 int element_rnd = EL_UNKNOWN;
7780 if (element_sp >= 0x00 &&
7782 element_rnd = EL_SP_START + element_sp;
7783 else if (element_sp == 0x28)
7784 element_rnd = EL_INVISIBLE_WALL;
7789 int map_action_SP_to_RND(int action_sp)
7793 case actActive: return ACTION_ACTIVE;
7794 case actImpact: return ACTION_IMPACT;
7795 case actExploding: return ACTION_EXPLODING;
7796 case actDigging: return ACTION_DIGGING;
7797 case actSnapping: return ACTION_SNAPPING;
7798 case actCollecting: return ACTION_COLLECTING;
7799 case actPassing: return ACTION_PASSING;
7800 case actPushing: return ACTION_PUSHING;
7801 case actDropping: return ACTION_DROPPING;
7803 default: return ACTION_DEFAULT;
7807 int map_element_RND_to_MM(int element_rnd)
7809 return (element_rnd >= EL_MM_START_1 &&
7810 element_rnd <= EL_MM_END_1 ?
7811 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7813 element_rnd >= EL_MM_START_2 &&
7814 element_rnd <= EL_MM_END_2 ?
7815 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7817 element_rnd >= EL_CHAR_START &&
7818 element_rnd <= EL_CHAR_END ?
7819 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7821 element_rnd >= EL_MM_RUNTIME_START &&
7822 element_rnd <= EL_MM_RUNTIME_END ?
7823 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7825 element_rnd >= EL_MM_DUMMY_START &&
7826 element_rnd <= EL_MM_DUMMY_END ?
7827 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7829 EL_MM_EMPTY_NATIVE);
7832 int map_element_MM_to_RND(int element_mm)
7834 return (element_mm == EL_MM_EMPTY_NATIVE ||
7835 element_mm == EL_DF_EMPTY_NATIVE ?
7838 element_mm >= EL_MM_START_1_NATIVE &&
7839 element_mm <= EL_MM_END_1_NATIVE ?
7840 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7842 element_mm >= EL_MM_START_2_NATIVE &&
7843 element_mm <= EL_MM_END_2_NATIVE ?
7844 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7846 element_mm >= EL_MM_CHAR_START_NATIVE &&
7847 element_mm <= EL_MM_CHAR_END_NATIVE ?
7848 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7850 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7851 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7852 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7854 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7855 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7856 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7861 int map_action_MM_to_RND(int action_mm)
7863 // all MM actions are defined to exactly match their RND counterparts
7867 int map_sound_MM_to_RND(int sound_mm)
7871 case SND_MM_GAME_LEVELTIME_CHARGING:
7872 return SND_GAME_LEVELTIME_CHARGING;
7874 case SND_MM_GAME_HEALTH_CHARGING:
7875 return SND_GAME_HEALTH_CHARGING;
7878 return SND_UNDEFINED;
7882 int map_mm_wall_element(int element)
7884 return (element >= EL_MM_STEEL_WALL_START &&
7885 element <= EL_MM_STEEL_WALL_END ?
7888 element >= EL_MM_WOODEN_WALL_START &&
7889 element <= EL_MM_WOODEN_WALL_END ?
7892 element >= EL_MM_ICE_WALL_START &&
7893 element <= EL_MM_ICE_WALL_END ?
7896 element >= EL_MM_AMOEBA_WALL_START &&
7897 element <= EL_MM_AMOEBA_WALL_END ?
7900 element >= EL_DF_STEEL_WALL_START &&
7901 element <= EL_DF_STEEL_WALL_END ?
7904 element >= EL_DF_WOODEN_WALL_START &&
7905 element <= EL_DF_WOODEN_WALL_END ?
7911 int map_mm_wall_element_editor(int element)
7915 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7916 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7917 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7918 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7919 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7920 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7922 default: return element;
7926 int get_next_element(int element)
7930 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7931 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7932 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7933 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7934 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7935 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7936 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7937 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7938 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7939 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7940 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7942 default: return element;
7946 int el2img_mm(int element_mm)
7948 return el2img(map_element_MM_to_RND(element_mm));
7951 int el_act_dir2img(int element, int action, int direction)
7953 element = GFX_ELEMENT(element);
7954 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7956 // direction_graphic[][] == graphic[] for undefined direction graphics
7957 return element_info[element].direction_graphic[action][direction];
7960 static int el_act_dir2crm(int element, int action, int direction)
7962 element = GFX_ELEMENT(element);
7963 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7965 // direction_graphic[][] == graphic[] for undefined direction graphics
7966 return element_info[element].direction_crumbled[action][direction];
7969 int el_act2img(int element, int action)
7971 element = GFX_ELEMENT(element);
7973 return element_info[element].graphic[action];
7976 int el_act2crm(int element, int action)
7978 element = GFX_ELEMENT(element);
7980 return element_info[element].crumbled[action];
7983 int el_dir2img(int element, int direction)
7985 element = GFX_ELEMENT(element);
7987 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7990 int el2baseimg(int element)
7992 return element_info[element].graphic[ACTION_DEFAULT];
7995 int el2img(int element)
7997 element = GFX_ELEMENT(element);
7999 return element_info[element].graphic[ACTION_DEFAULT];
8002 int el2edimg(int element)
8004 element = GFX_ELEMENT(element);
8006 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8009 int el2preimg(int element)
8011 element = GFX_ELEMENT(element);
8013 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8016 int el2panelimg(int element)
8018 element = GFX_ELEMENT(element);
8020 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8023 int font2baseimg(int font_nr)
8025 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8028 int getBeltNrFromBeltElement(int element)
8030 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8031 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8032 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8035 int getBeltNrFromBeltActiveElement(int element)
8037 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8038 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8039 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8042 int getBeltNrFromBeltSwitchElement(int element)
8044 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8045 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8046 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8049 int getBeltDirNrFromBeltElement(int element)
8051 static int belt_base_element[4] =
8053 EL_CONVEYOR_BELT_1_LEFT,
8054 EL_CONVEYOR_BELT_2_LEFT,
8055 EL_CONVEYOR_BELT_3_LEFT,
8056 EL_CONVEYOR_BELT_4_LEFT
8059 int belt_nr = getBeltNrFromBeltElement(element);
8060 int belt_dir_nr = element - belt_base_element[belt_nr];
8062 return (belt_dir_nr % 3);
8065 int getBeltDirNrFromBeltSwitchElement(int element)
8067 static int belt_base_element[4] =
8069 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8070 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8071 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8072 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8075 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8076 int belt_dir_nr = element - belt_base_element[belt_nr];
8078 return (belt_dir_nr % 3);
8081 int getBeltDirFromBeltElement(int element)
8083 static int belt_move_dir[3] =
8090 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8092 return belt_move_dir[belt_dir_nr];
8095 int getBeltDirFromBeltSwitchElement(int element)
8097 static int belt_move_dir[3] =
8104 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8106 return belt_move_dir[belt_dir_nr];
8109 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8111 static int belt_base_element[4] =
8113 EL_CONVEYOR_BELT_1_LEFT,
8114 EL_CONVEYOR_BELT_2_LEFT,
8115 EL_CONVEYOR_BELT_3_LEFT,
8116 EL_CONVEYOR_BELT_4_LEFT
8119 return belt_base_element[belt_nr] + belt_dir_nr;
8122 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8124 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8126 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8129 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8131 static int belt_base_element[4] =
8133 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8134 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8135 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8136 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8139 return belt_base_element[belt_nr] + belt_dir_nr;
8142 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8144 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8146 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8149 boolean getTeamMode_EM(void)
8151 return game.team_mode || network_playing;
8154 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8156 int game_frame_delay_value;
8158 game_frame_delay_value =
8159 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8160 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8163 if (tape.playing && tape.warp_forward && !tape.pausing)
8164 game_frame_delay_value = 0;
8166 return game_frame_delay_value;
8169 unsigned int InitRND(int seed)
8171 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8172 return InitEngineRandom_EM(seed);
8173 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8174 return InitEngineRandom_SP(seed);
8175 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8176 return InitEngineRandom_MM(seed);
8178 return InitEngineRandom_RND(seed);
8181 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8182 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8184 static int get_effective_element_EM(int tile, int frame_em)
8186 int element = object_mapping[tile].element_rnd;
8187 int action = object_mapping[tile].action;
8188 boolean is_backside = object_mapping[tile].is_backside;
8189 boolean action_removing = (action == ACTION_DIGGING ||
8190 action == ACTION_SNAPPING ||
8191 action == ACTION_COLLECTING);
8197 case Yacid_splash_eB:
8198 case Yacid_splash_wB:
8199 return (frame_em > 5 ? EL_EMPTY : element);
8205 else // frame_em == 7
8209 case Yacid_splash_eB:
8210 case Yacid_splash_wB:
8213 case Yemerald_stone:
8216 case Ydiamond_stone:
8220 case Xdrip_stretchB:
8239 case Xsand_stonein_1:
8240 case Xsand_stonein_2:
8241 case Xsand_stonein_3:
8242 case Xsand_stonein_4:
8246 return (is_backside || action_removing ? EL_EMPTY : element);
8251 static boolean check_linear_animation_EM(int tile)
8255 case Xsand_stonesand_1:
8256 case Xsand_stonesand_quickout_1:
8257 case Xsand_sandstone_1:
8258 case Xsand_stonein_1:
8259 case Xsand_stoneout_1:
8278 case Yacid_splash_eB:
8279 case Yacid_splash_wB:
8280 case Yemerald_stone:
8287 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8288 boolean has_crumbled_graphics,
8289 int crumbled, int sync_frame)
8291 // if element can be crumbled, but certain action graphics are just empty
8292 // space (like instantly snapping sand to empty space in 1 frame), do not
8293 // treat these empty space graphics as crumbled graphics in EMC engine
8294 if (crumbled == IMG_EMPTY_SPACE)
8295 has_crumbled_graphics = FALSE;
8297 if (has_crumbled_graphics)
8299 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8300 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8301 g_crumbled->anim_delay,
8302 g_crumbled->anim_mode,
8303 g_crumbled->anim_start_frame,
8306 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8307 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8309 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8310 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8312 g_em->has_crumbled_graphics = TRUE;
8316 g_em->crumbled_bitmap = NULL;
8317 g_em->crumbled_src_x = 0;
8318 g_em->crumbled_src_y = 0;
8319 g_em->crumbled_border_size = 0;
8320 g_em->crumbled_tile_size = 0;
8322 g_em->has_crumbled_graphics = FALSE;
8327 void ResetGfxAnimation_EM(int x, int y, int tile)
8333 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8334 int tile, int frame_em, int x, int y)
8336 int action = object_mapping[tile].action;
8337 int direction = object_mapping[tile].direction;
8338 int effective_element = get_effective_element_EM(tile, frame_em);
8339 int graphic = (direction == MV_NONE ?
8340 el_act2img(effective_element, action) :
8341 el_act_dir2img(effective_element, action, direction));
8342 struct GraphicInfo *g = &graphic_info[graphic];
8344 boolean action_removing = (action == ACTION_DIGGING ||
8345 action == ACTION_SNAPPING ||
8346 action == ACTION_COLLECTING);
8347 boolean action_moving = (action == ACTION_FALLING ||
8348 action == ACTION_MOVING ||
8349 action == ACTION_PUSHING ||
8350 action == ACTION_EATING ||
8351 action == ACTION_FILLING ||
8352 action == ACTION_EMPTYING);
8353 boolean action_falling = (action == ACTION_FALLING ||
8354 action == ACTION_FILLING ||
8355 action == ACTION_EMPTYING);
8357 // special case: graphic uses "2nd movement tile" and has defined
8358 // 7 frames for movement animation (or less) => use default graphic
8359 // for last (8th) frame which ends the movement animation
8360 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8362 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8363 graphic = (direction == MV_NONE ?
8364 el_act2img(effective_element, action) :
8365 el_act_dir2img(effective_element, action, direction));
8367 g = &graphic_info[graphic];
8370 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8374 else if (action_moving)
8376 boolean is_backside = object_mapping[tile].is_backside;
8380 int direction = object_mapping[tile].direction;
8381 int move_dir = (action_falling ? MV_DOWN : direction);
8386 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8387 if (g->double_movement && frame_em == 0)
8391 if (move_dir == MV_LEFT)
8392 GfxFrame[x - 1][y] = GfxFrame[x][y];
8393 else if (move_dir == MV_RIGHT)
8394 GfxFrame[x + 1][y] = GfxFrame[x][y];
8395 else if (move_dir == MV_UP)
8396 GfxFrame[x][y - 1] = GfxFrame[x][y];
8397 else if (move_dir == MV_DOWN)
8398 GfxFrame[x][y + 1] = GfxFrame[x][y];
8405 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8406 if (tile == Xsand_stonesand_quickout_1 ||
8407 tile == Xsand_stonesand_quickout_2)
8411 if (graphic_info[graphic].anim_global_sync)
8412 sync_frame = FrameCounter;
8413 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8414 sync_frame = GfxFrame[x][y];
8416 sync_frame = 0; // playfield border (pseudo steel)
8418 SetRandomAnimationValue(x, y);
8420 int frame = getAnimationFrame(g->anim_frames,
8423 g->anim_start_frame,
8426 g_em->unique_identifier =
8427 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8430 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8431 int tile, int frame_em, int x, int y)
8433 int action = object_mapping[tile].action;
8434 int direction = object_mapping[tile].direction;
8435 boolean is_backside = object_mapping[tile].is_backside;
8436 int effective_element = get_effective_element_EM(tile, frame_em);
8437 int effective_action = action;
8438 int graphic = (direction == MV_NONE ?
8439 el_act2img(effective_element, effective_action) :
8440 el_act_dir2img(effective_element, effective_action,
8442 int crumbled = (direction == MV_NONE ?
8443 el_act2crm(effective_element, effective_action) :
8444 el_act_dir2crm(effective_element, effective_action,
8446 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8447 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8448 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8449 struct GraphicInfo *g = &graphic_info[graphic];
8452 // special case: graphic uses "2nd movement tile" and has defined
8453 // 7 frames for movement animation (or less) => use default graphic
8454 // for last (8th) frame which ends the movement animation
8455 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8457 effective_action = ACTION_DEFAULT;
8458 graphic = (direction == MV_NONE ?
8459 el_act2img(effective_element, effective_action) :
8460 el_act_dir2img(effective_element, effective_action,
8462 crumbled = (direction == MV_NONE ?
8463 el_act2crm(effective_element, effective_action) :
8464 el_act_dir2crm(effective_element, effective_action,
8467 g = &graphic_info[graphic];
8470 if (graphic_info[graphic].anim_global_sync)
8471 sync_frame = FrameCounter;
8472 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8473 sync_frame = GfxFrame[x][y];
8475 sync_frame = 0; // playfield border (pseudo steel)
8477 SetRandomAnimationValue(x, y);
8479 int frame = getAnimationFrame(g->anim_frames,
8482 g->anim_start_frame,
8485 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8486 g->double_movement && is_backside);
8488 // (updating the "crumbled" graphic definitions is probably not really needed,
8489 // as animations for crumbled graphics can't be longer than one EMC cycle)
8490 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8494 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8495 int player_nr, int anim, int frame_em)
8497 int element = player_mapping[player_nr][anim].element_rnd;
8498 int action = player_mapping[player_nr][anim].action;
8499 int direction = player_mapping[player_nr][anim].direction;
8500 int graphic = (direction == MV_NONE ?
8501 el_act2img(element, action) :
8502 el_act_dir2img(element, action, direction));
8503 struct GraphicInfo *g = &graphic_info[graphic];
8506 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8508 stored_player[player_nr].StepFrame = frame_em;
8510 sync_frame = stored_player[player_nr].Frame;
8512 int frame = getAnimationFrame(g->anim_frames,
8515 g->anim_start_frame,
8518 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8519 &g_em->src_x, &g_em->src_y, FALSE);
8522 void InitGraphicInfo_EM(void)
8527 int num_em_gfx_errors = 0;
8529 if (graphic_info_em_object[0][0].bitmap == NULL)
8531 // EM graphics not yet initialized in em_open_all()
8536 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8539 // always start with reliable default values
8540 for (i = 0; i < TILE_MAX; i++)
8542 object_mapping[i].element_rnd = EL_UNKNOWN;
8543 object_mapping[i].is_backside = FALSE;
8544 object_mapping[i].action = ACTION_DEFAULT;
8545 object_mapping[i].direction = MV_NONE;
8548 // always start with reliable default values
8549 for (p = 0; p < MAX_PLAYERS; p++)
8551 for (i = 0; i < SPR_MAX; i++)
8553 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8554 player_mapping[p][i].action = ACTION_DEFAULT;
8555 player_mapping[p][i].direction = MV_NONE;
8559 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8561 int e = em_object_mapping_list[i].element_em;
8563 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8564 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8566 if (em_object_mapping_list[i].action != -1)
8567 object_mapping[e].action = em_object_mapping_list[i].action;
8569 if (em_object_mapping_list[i].direction != -1)
8570 object_mapping[e].direction =
8571 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8574 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8576 int a = em_player_mapping_list[i].action_em;
8577 int p = em_player_mapping_list[i].player_nr;
8579 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8581 if (em_player_mapping_list[i].action != -1)
8582 player_mapping[p][a].action = em_player_mapping_list[i].action;
8584 if (em_player_mapping_list[i].direction != -1)
8585 player_mapping[p][a].direction =
8586 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8589 for (i = 0; i < TILE_MAX; i++)
8591 int element = object_mapping[i].element_rnd;
8592 int action = object_mapping[i].action;
8593 int direction = object_mapping[i].direction;
8594 boolean is_backside = object_mapping[i].is_backside;
8595 boolean action_exploding = ((action == ACTION_EXPLODING ||
8596 action == ACTION_SMASHED_BY_ROCK ||
8597 action == ACTION_SMASHED_BY_SPRING) &&
8598 element != EL_DIAMOND);
8599 boolean action_active = (action == ACTION_ACTIVE);
8600 boolean action_other = (action == ACTION_OTHER);
8602 for (j = 0; j < 8; j++)
8604 int effective_element = get_effective_element_EM(i, j);
8605 int effective_action = (j < 7 ? action :
8606 i == Xdrip_stretch ? action :
8607 i == Xdrip_stretchB ? action :
8608 i == Ydrip_s1 ? action :
8609 i == Ydrip_s1B ? action :
8610 i == Xball_1B ? action :
8611 i == Xball_2 ? action :
8612 i == Xball_2B ? action :
8613 i == Yball_eat ? action :
8614 i == Ykey_1_eat ? action :
8615 i == Ykey_2_eat ? action :
8616 i == Ykey_3_eat ? action :
8617 i == Ykey_4_eat ? action :
8618 i == Ykey_5_eat ? action :
8619 i == Ykey_6_eat ? action :
8620 i == Ykey_7_eat ? action :
8621 i == Ykey_8_eat ? action :
8622 i == Ylenses_eat ? action :
8623 i == Ymagnify_eat ? action :
8624 i == Ygrass_eat ? action :
8625 i == Ydirt_eat ? action :
8626 i == Xsand_stonein_1 ? action :
8627 i == Xsand_stonein_2 ? action :
8628 i == Xsand_stonein_3 ? action :
8629 i == Xsand_stonein_4 ? action :
8630 i == Xsand_stoneout_1 ? action :
8631 i == Xsand_stoneout_2 ? action :
8632 i == Xboom_android ? ACTION_EXPLODING :
8633 action_exploding ? ACTION_EXPLODING :
8634 action_active ? action :
8635 action_other ? action :
8637 int graphic = (el_act_dir2img(effective_element, effective_action,
8639 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8641 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8642 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8643 boolean has_action_graphics = (graphic != base_graphic);
8644 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8645 struct GraphicInfo *g = &graphic_info[graphic];
8646 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8649 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8650 boolean special_animation = (action != ACTION_DEFAULT &&
8651 g->anim_frames == 3 &&
8652 g->anim_delay == 2 &&
8653 g->anim_mode & ANIM_LINEAR);
8654 int sync_frame = (i == Xdrip_stretch ? 7 :
8655 i == Xdrip_stretchB ? 7 :
8656 i == Ydrip_s2 ? j + 8 :
8657 i == Ydrip_s2B ? j + 8 :
8666 i == Xfake_acid_1 ? 0 :
8667 i == Xfake_acid_2 ? 10 :
8668 i == Xfake_acid_3 ? 20 :
8669 i == Xfake_acid_4 ? 30 :
8670 i == Xfake_acid_5 ? 40 :
8671 i == Xfake_acid_6 ? 50 :
8672 i == Xfake_acid_7 ? 60 :
8673 i == Xfake_acid_8 ? 70 :
8675 i == Xball_2B ? j + 8 :
8676 i == Yball_eat ? j + 1 :
8677 i == Ykey_1_eat ? j + 1 :
8678 i == Ykey_2_eat ? j + 1 :
8679 i == Ykey_3_eat ? j + 1 :
8680 i == Ykey_4_eat ? j + 1 :
8681 i == Ykey_5_eat ? j + 1 :
8682 i == Ykey_6_eat ? j + 1 :
8683 i == Ykey_7_eat ? j + 1 :
8684 i == Ykey_8_eat ? j + 1 :
8685 i == Ylenses_eat ? j + 1 :
8686 i == Ymagnify_eat ? j + 1 :
8687 i == Ygrass_eat ? j + 1 :
8688 i == Ydirt_eat ? j + 1 :
8689 i == Xamoeba_1 ? 0 :
8690 i == Xamoeba_2 ? 1 :
8691 i == Xamoeba_3 ? 2 :
8692 i == Xamoeba_4 ? 3 :
8693 i == Xamoeba_5 ? 0 :
8694 i == Xamoeba_6 ? 1 :
8695 i == Xamoeba_7 ? 2 :
8696 i == Xamoeba_8 ? 3 :
8697 i == Xexit_2 ? j + 8 :
8698 i == Xexit_3 ? j + 16 :
8699 i == Xdynamite_1 ? 0 :
8700 i == Xdynamite_2 ? 8 :
8701 i == Xdynamite_3 ? 16 :
8702 i == Xdynamite_4 ? 24 :
8703 i == Xsand_stonein_1 ? j + 1 :
8704 i == Xsand_stonein_2 ? j + 9 :
8705 i == Xsand_stonein_3 ? j + 17 :
8706 i == Xsand_stonein_4 ? j + 25 :
8707 i == Xsand_stoneout_1 && j == 0 ? 0 :
8708 i == Xsand_stoneout_1 && j == 1 ? 0 :
8709 i == Xsand_stoneout_1 && j == 2 ? 1 :
8710 i == Xsand_stoneout_1 && j == 3 ? 2 :
8711 i == Xsand_stoneout_1 && j == 4 ? 2 :
8712 i == Xsand_stoneout_1 && j == 5 ? 3 :
8713 i == Xsand_stoneout_1 && j == 6 ? 4 :
8714 i == Xsand_stoneout_1 && j == 7 ? 4 :
8715 i == Xsand_stoneout_2 && j == 0 ? 5 :
8716 i == Xsand_stoneout_2 && j == 1 ? 6 :
8717 i == Xsand_stoneout_2 && j == 2 ? 7 :
8718 i == Xsand_stoneout_2 && j == 3 ? 8 :
8719 i == Xsand_stoneout_2 && j == 4 ? 9 :
8720 i == Xsand_stoneout_2 && j == 5 ? 11 :
8721 i == Xsand_stoneout_2 && j == 6 ? 13 :
8722 i == Xsand_stoneout_2 && j == 7 ? 15 :
8723 i == Xboom_bug && j == 1 ? 2 :
8724 i == Xboom_bug && j == 2 ? 2 :
8725 i == Xboom_bug && j == 3 ? 4 :
8726 i == Xboom_bug && j == 4 ? 4 :
8727 i == Xboom_bug && j == 5 ? 2 :
8728 i == Xboom_bug && j == 6 ? 2 :
8729 i == Xboom_bug && j == 7 ? 0 :
8730 i == Xboom_bomb && j == 1 ? 2 :
8731 i == Xboom_bomb && j == 2 ? 2 :
8732 i == Xboom_bomb && j == 3 ? 4 :
8733 i == Xboom_bomb && j == 4 ? 4 :
8734 i == Xboom_bomb && j == 5 ? 2 :
8735 i == Xboom_bomb && j == 6 ? 2 :
8736 i == Xboom_bomb && j == 7 ? 0 :
8737 i == Xboom_android && j == 7 ? 6 :
8738 i == Xboom_1 && j == 1 ? 2 :
8739 i == Xboom_1 && j == 2 ? 2 :
8740 i == Xboom_1 && j == 3 ? 4 :
8741 i == Xboom_1 && j == 4 ? 4 :
8742 i == Xboom_1 && j == 5 ? 6 :
8743 i == Xboom_1 && j == 6 ? 6 :
8744 i == Xboom_1 && j == 7 ? 8 :
8745 i == Xboom_2 && j == 0 ? 8 :
8746 i == Xboom_2 && j == 1 ? 8 :
8747 i == Xboom_2 && j == 2 ? 10 :
8748 i == Xboom_2 && j == 3 ? 10 :
8749 i == Xboom_2 && j == 4 ? 10 :
8750 i == Xboom_2 && j == 5 ? 12 :
8751 i == Xboom_2 && j == 6 ? 12 :
8752 i == Xboom_2 && j == 7 ? 12 :
8753 special_animation && j == 4 ? 3 :
8754 effective_action != action ? 0 :
8758 Bitmap *debug_bitmap = g_em->bitmap;
8759 int debug_src_x = g_em->src_x;
8760 int debug_src_y = g_em->src_y;
8763 int frame = getAnimationFrame(g->anim_frames,
8766 g->anim_start_frame,
8769 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8770 g->double_movement && is_backside);
8772 g_em->bitmap = src_bitmap;
8773 g_em->src_x = src_x;
8774 g_em->src_y = src_y;
8775 g_em->src_offset_x = 0;
8776 g_em->src_offset_y = 0;
8777 g_em->dst_offset_x = 0;
8778 g_em->dst_offset_y = 0;
8779 g_em->width = TILEX;
8780 g_em->height = TILEY;
8782 g_em->preserve_background = FALSE;
8784 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8787 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8788 effective_action == ACTION_MOVING ||
8789 effective_action == ACTION_PUSHING ||
8790 effective_action == ACTION_EATING)) ||
8791 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8792 effective_action == ACTION_EMPTYING)))
8795 (effective_action == ACTION_FALLING ||
8796 effective_action == ACTION_FILLING ||
8797 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8798 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8799 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8800 int num_steps = (i == Ydrip_s1 ? 16 :
8801 i == Ydrip_s1B ? 16 :
8802 i == Ydrip_s2 ? 16 :
8803 i == Ydrip_s2B ? 16 :
8804 i == Xsand_stonein_1 ? 32 :
8805 i == Xsand_stonein_2 ? 32 :
8806 i == Xsand_stonein_3 ? 32 :
8807 i == Xsand_stonein_4 ? 32 :
8808 i == Xsand_stoneout_1 ? 16 :
8809 i == Xsand_stoneout_2 ? 16 : 8);
8810 int cx = ABS(dx) * (TILEX / num_steps);
8811 int cy = ABS(dy) * (TILEY / num_steps);
8812 int step_frame = (i == Ydrip_s2 ? j + 8 :
8813 i == Ydrip_s2B ? j + 8 :
8814 i == Xsand_stonein_2 ? j + 8 :
8815 i == Xsand_stonein_3 ? j + 16 :
8816 i == Xsand_stonein_4 ? j + 24 :
8817 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8818 int step = (is_backside ? step_frame : num_steps - step_frame);
8820 if (is_backside) // tile where movement starts
8822 if (dx < 0 || dy < 0)
8824 g_em->src_offset_x = cx * step;
8825 g_em->src_offset_y = cy * step;
8829 g_em->dst_offset_x = cx * step;
8830 g_em->dst_offset_y = cy * step;
8833 else // tile where movement ends
8835 if (dx < 0 || dy < 0)
8837 g_em->dst_offset_x = cx * step;
8838 g_em->dst_offset_y = cy * step;
8842 g_em->src_offset_x = cx * step;
8843 g_em->src_offset_y = cy * step;
8847 g_em->width = TILEX - cx * step;
8848 g_em->height = TILEY - cy * step;
8851 // create unique graphic identifier to decide if tile must be redrawn
8852 /* bit 31 - 16 (16 bit): EM style graphic
8853 bit 15 - 12 ( 4 bit): EM style frame
8854 bit 11 - 6 ( 6 bit): graphic width
8855 bit 5 - 0 ( 6 bit): graphic height */
8856 g_em->unique_identifier =
8857 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8861 // skip check for EMC elements not contained in original EMC artwork
8862 if (element == EL_EMC_FAKE_ACID)
8865 if (g_em->bitmap != debug_bitmap ||
8866 g_em->src_x != debug_src_x ||
8867 g_em->src_y != debug_src_y ||
8868 g_em->src_offset_x != 0 ||
8869 g_em->src_offset_y != 0 ||
8870 g_em->dst_offset_x != 0 ||
8871 g_em->dst_offset_y != 0 ||
8872 g_em->width != TILEX ||
8873 g_em->height != TILEY)
8875 static int last_i = -1;
8883 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8884 i, element, element_info[element].token_name,
8885 element_action_info[effective_action].suffix, direction);
8887 if (element != effective_element)
8888 printf(" [%d ('%s')]",
8890 element_info[effective_element].token_name);
8894 if (g_em->bitmap != debug_bitmap)
8895 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8896 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8898 if (g_em->src_x != debug_src_x ||
8899 g_em->src_y != debug_src_y)
8900 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8901 j, (is_backside ? 'B' : 'F'),
8902 g_em->src_x, g_em->src_y,
8903 g_em->src_x / 32, g_em->src_y / 32,
8904 debug_src_x, debug_src_y,
8905 debug_src_x / 32, debug_src_y / 32);
8907 if (g_em->src_offset_x != 0 ||
8908 g_em->src_offset_y != 0 ||
8909 g_em->dst_offset_x != 0 ||
8910 g_em->dst_offset_y != 0)
8911 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8913 g_em->src_offset_x, g_em->src_offset_y,
8914 g_em->dst_offset_x, g_em->dst_offset_y);
8916 if (g_em->width != TILEX ||
8917 g_em->height != TILEY)
8918 printf(" %d (%d): size %d,%d should be %d,%d\n",
8920 g_em->width, g_em->height, TILEX, TILEY);
8922 num_em_gfx_errors++;
8929 for (i = 0; i < TILE_MAX; i++)
8931 for (j = 0; j < 8; j++)
8933 int element = object_mapping[i].element_rnd;
8934 int action = object_mapping[i].action;
8935 int direction = object_mapping[i].direction;
8936 boolean is_backside = object_mapping[i].is_backside;
8937 int graphic_action = el_act_dir2img(element, action, direction);
8938 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8940 if ((action == ACTION_SMASHED_BY_ROCK ||
8941 action == ACTION_SMASHED_BY_SPRING ||
8942 action == ACTION_EATING) &&
8943 graphic_action == graphic_default)
8945 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8946 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8947 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8948 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8951 // no separate animation for "smashed by rock" -- use rock instead
8952 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8953 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8955 g_em->bitmap = g_xx->bitmap;
8956 g_em->src_x = g_xx->src_x;
8957 g_em->src_y = g_xx->src_y;
8958 g_em->src_offset_x = g_xx->src_offset_x;
8959 g_em->src_offset_y = g_xx->src_offset_y;
8960 g_em->dst_offset_x = g_xx->dst_offset_x;
8961 g_em->dst_offset_y = g_xx->dst_offset_y;
8962 g_em->width = g_xx->width;
8963 g_em->height = g_xx->height;
8964 g_em->unique_identifier = g_xx->unique_identifier;
8967 g_em->preserve_background = TRUE;
8972 for (p = 0; p < MAX_PLAYERS; p++)
8974 for (i = 0; i < SPR_MAX; i++)
8976 int element = player_mapping[p][i].element_rnd;
8977 int action = player_mapping[p][i].action;
8978 int direction = player_mapping[p][i].direction;
8980 for (j = 0; j < 8; j++)
8982 int effective_element = element;
8983 int effective_action = action;
8984 int graphic = (direction == MV_NONE ?
8985 el_act2img(effective_element, effective_action) :
8986 el_act_dir2img(effective_element, effective_action,
8988 struct GraphicInfo *g = &graphic_info[graphic];
8989 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8995 Bitmap *debug_bitmap = g_em->bitmap;
8996 int debug_src_x = g_em->src_x;
8997 int debug_src_y = g_em->src_y;
9000 int frame = getAnimationFrame(g->anim_frames,
9003 g->anim_start_frame,
9006 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9008 g_em->bitmap = src_bitmap;
9009 g_em->src_x = src_x;
9010 g_em->src_y = src_y;
9011 g_em->src_offset_x = 0;
9012 g_em->src_offset_y = 0;
9013 g_em->dst_offset_x = 0;
9014 g_em->dst_offset_y = 0;
9015 g_em->width = TILEX;
9016 g_em->height = TILEY;
9020 // skip check for EMC elements not contained in original EMC artwork
9021 if (element == EL_PLAYER_3 ||
9022 element == EL_PLAYER_4)
9025 if (g_em->bitmap != debug_bitmap ||
9026 g_em->src_x != debug_src_x ||
9027 g_em->src_y != debug_src_y)
9029 static int last_i = -1;
9037 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9038 p, i, element, element_info[element].token_name,
9039 element_action_info[effective_action].suffix, direction);
9041 if (element != effective_element)
9042 printf(" [%d ('%s')]",
9044 element_info[effective_element].token_name);
9048 if (g_em->bitmap != debug_bitmap)
9049 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9050 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9052 if (g_em->src_x != debug_src_x ||
9053 g_em->src_y != debug_src_y)
9054 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9056 g_em->src_x, g_em->src_y,
9057 g_em->src_x / 32, g_em->src_y / 32,
9058 debug_src_x, debug_src_y,
9059 debug_src_x / 32, debug_src_y / 32);
9061 num_em_gfx_errors++;
9071 printf("::: [%d errors found]\n", num_em_gfx_errors);
9077 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9078 boolean any_player_moving,
9079 boolean any_player_snapping,
9080 boolean any_player_dropping)
9082 if (frame == 0 && !any_player_dropping)
9084 if (!local_player->was_waiting)
9086 if (!CheckSaveEngineSnapshotToList())
9089 local_player->was_waiting = TRUE;
9092 else if (any_player_moving || any_player_snapping || any_player_dropping)
9094 local_player->was_waiting = FALSE;
9098 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9099 boolean murphy_is_dropping)
9101 if (murphy_is_waiting)
9103 if (!local_player->was_waiting)
9105 if (!CheckSaveEngineSnapshotToList())
9108 local_player->was_waiting = TRUE;
9113 local_player->was_waiting = FALSE;
9117 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9118 boolean button_released)
9120 if (button_released)
9122 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9123 CheckSaveEngineSnapshotToList();
9125 else if (element_clicked)
9127 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9128 CheckSaveEngineSnapshotToList();
9130 game.snapshot.changed_action = TRUE;
9134 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9135 boolean any_player_moving,
9136 boolean any_player_snapping,
9137 boolean any_player_dropping)
9139 if (tape.single_step && tape.recording && !tape.pausing)
9140 if (frame == 0 && !any_player_dropping)
9141 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9143 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9144 any_player_snapping, any_player_dropping);
9147 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9148 boolean murphy_is_dropping)
9150 boolean murphy_starts_dropping = FALSE;
9153 for (i = 0; i < MAX_PLAYERS; i++)
9154 if (stored_player[i].force_dropping)
9155 murphy_starts_dropping = TRUE;
9157 if (tape.single_step && tape.recording && !tape.pausing)
9158 if (murphy_is_waiting && !murphy_starts_dropping)
9159 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9161 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9164 void CheckSingleStepMode_MM(boolean element_clicked,
9165 boolean button_released)
9167 if (tape.single_step && tape.recording && !tape.pausing)
9168 if (button_released)
9169 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9171 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9174 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9175 int graphic, int sync_frame, int x, int y)
9177 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9179 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9182 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9184 return (IS_NEXT_FRAME(sync_frame, graphic));
9187 int getGraphicInfo_Delay(int graphic)
9189 return graphic_info[graphic].anim_delay;
9192 void PlayMenuSoundExt(int sound)
9194 if (sound == SND_UNDEFINED)
9197 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9198 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9201 if (IS_LOOP_SOUND(sound))
9202 PlaySoundLoop(sound);
9207 void PlayMenuSound(void)
9209 PlayMenuSoundExt(menu.sound[game_status]);
9212 void PlayMenuSoundStereo(int sound, int stereo_position)
9214 if (sound == SND_UNDEFINED)
9217 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9218 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9221 if (IS_LOOP_SOUND(sound))
9222 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9224 PlaySoundStereo(sound, stereo_position);
9227 void PlayMenuSoundIfLoopExt(int sound)
9229 if (sound == SND_UNDEFINED)
9232 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9233 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9236 if (IS_LOOP_SOUND(sound))
9237 PlaySoundLoop(sound);
9240 void PlayMenuSoundIfLoop(void)
9242 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9245 void PlayMenuMusicExt(int music)
9247 if (music == MUS_UNDEFINED)
9250 if (!setup.sound_music)
9253 if (IS_LOOP_MUSIC(music))
9254 PlayMusicLoop(music);
9259 void PlayMenuMusic(void)
9261 char *curr_music = getCurrentlyPlayingMusicFilename();
9262 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9264 if (!strEqual(curr_music, next_music))
9265 PlayMenuMusicExt(menu.music[game_status]);
9268 void PlayMenuSoundsAndMusic(void)
9274 static void FadeMenuSounds(void)
9279 static void FadeMenuMusic(void)
9281 char *curr_music = getCurrentlyPlayingMusicFilename();
9282 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9284 if (!strEqual(curr_music, next_music))
9288 void FadeMenuSoundsAndMusic(void)
9294 void PlaySoundActivating(void)
9297 PlaySound(SND_MENU_ITEM_ACTIVATING);
9301 void PlaySoundSelecting(void)
9304 PlaySound(SND_MENU_ITEM_SELECTING);
9308 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9310 boolean change_fullscreen = (setup.fullscreen !=
9311 video.fullscreen_enabled);
9312 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9313 setup.window_scaling_percent !=
9314 video.window_scaling_percent);
9316 if (change_window_scaling_percent && video.fullscreen_enabled)
9319 if (!change_window_scaling_percent && !video.fullscreen_available)
9322 if (change_window_scaling_percent)
9324 SDLSetWindowScaling(setup.window_scaling_percent);
9328 else if (change_fullscreen)
9330 SDLSetWindowFullscreen(setup.fullscreen);
9332 // set setup value according to successfully changed fullscreen mode
9333 setup.fullscreen = video.fullscreen_enabled;
9338 if (change_fullscreen ||
9339 change_window_scaling_percent)
9341 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9343 // save backbuffer content which gets lost when toggling fullscreen mode
9344 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9346 if (change_window_scaling_percent)
9348 // keep window mode, but change window scaling
9349 video.fullscreen_enabled = TRUE; // force new window scaling
9352 // toggle fullscreen
9353 ChangeVideoModeIfNeeded(setup.fullscreen);
9355 // set setup value according to successfully changed fullscreen mode
9356 setup.fullscreen = video.fullscreen_enabled;
9358 // restore backbuffer content from temporary backbuffer backup bitmap
9359 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9361 FreeBitmap(tmp_backbuffer);
9363 // update visible window/screen
9364 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9368 static void JoinRectangles(int *x, int *y, int *width, int *height,
9369 int x2, int y2, int width2, int height2)
9371 // do not join with "off-screen" rectangle
9372 if (x2 == -1 || y2 == -1)
9377 *width = MAX(*width, width2);
9378 *height = MAX(*height, height2);
9381 void SetAnimStatus(int anim_status_new)
9383 if (anim_status_new == GAME_MODE_MAIN)
9384 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9385 else if (anim_status_new == GAME_MODE_SCORES)
9386 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9388 global.anim_status_next = anim_status_new;
9390 // directly set screen modes that are entered without fading
9391 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9392 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9393 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9394 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9395 global.anim_status = global.anim_status_next;
9398 void SetGameStatus(int game_status_new)
9400 if (game_status_new != game_status)
9401 game_status_last_screen = game_status;
9403 game_status = game_status_new;
9405 SetAnimStatus(game_status_new);
9408 void SetFontStatus(int game_status_new)
9410 static int last_game_status = -1;
9412 if (game_status_new != -1)
9414 // set game status for font use after storing last game status
9415 last_game_status = game_status;
9416 game_status = game_status_new;
9420 // reset game status after font use from last stored game status
9421 game_status = last_game_status;
9425 void ResetFontStatus(void)
9430 void SetLevelSetInfo(char *identifier, int level_nr)
9432 setString(&levelset.identifier, identifier);
9434 levelset.level_nr = level_nr;
9437 boolean CheckIfAllViewportsHaveChanged(void)
9439 // if game status has not changed, viewports have not changed either
9440 if (game_status == game_status_last)
9443 // check if all viewports have changed with current game status
9445 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9446 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9447 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9448 int new_real_sx = vp_playfield->x;
9449 int new_real_sy = vp_playfield->y;
9450 int new_full_sxsize = vp_playfield->width;
9451 int new_full_sysize = vp_playfield->height;
9452 int new_dx = vp_door_1->x;
9453 int new_dy = vp_door_1->y;
9454 int new_dxsize = vp_door_1->width;
9455 int new_dysize = vp_door_1->height;
9456 int new_vx = vp_door_2->x;
9457 int new_vy = vp_door_2->y;
9458 int new_vxsize = vp_door_2->width;
9459 int new_vysize = vp_door_2->height;
9461 boolean playfield_viewport_has_changed =
9462 (new_real_sx != REAL_SX ||
9463 new_real_sy != REAL_SY ||
9464 new_full_sxsize != FULL_SXSIZE ||
9465 new_full_sysize != FULL_SYSIZE);
9467 boolean door_1_viewport_has_changed =
9470 new_dxsize != DXSIZE ||
9471 new_dysize != DYSIZE);
9473 boolean door_2_viewport_has_changed =
9476 new_vxsize != VXSIZE ||
9477 new_vysize != VYSIZE ||
9478 game_status_last == GAME_MODE_EDITOR);
9480 return (playfield_viewport_has_changed &&
9481 door_1_viewport_has_changed &&
9482 door_2_viewport_has_changed);
9485 boolean CheckFadeAll(void)
9487 return (CheckIfGlobalBorderHasChanged() ||
9488 CheckIfAllViewportsHaveChanged());
9491 void ChangeViewportPropertiesIfNeeded(void)
9493 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9494 FALSE : setup.small_game_graphics);
9495 int gfx_game_mode = game_status;
9496 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9498 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9499 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9500 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9501 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9502 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9503 int new_win_xsize = vp_window->width;
9504 int new_win_ysize = vp_window->height;
9505 int border_left = vp_playfield->border_left;
9506 int border_right = vp_playfield->border_right;
9507 int border_top = vp_playfield->border_top;
9508 int border_bottom = vp_playfield->border_bottom;
9509 int new_sx = vp_playfield->x + border_left;
9510 int new_sy = vp_playfield->y + border_top;
9511 int new_sxsize = vp_playfield->width - border_left - border_right;
9512 int new_sysize = vp_playfield->height - border_top - border_bottom;
9513 int new_real_sx = vp_playfield->x;
9514 int new_real_sy = vp_playfield->y;
9515 int new_full_sxsize = vp_playfield->width;
9516 int new_full_sysize = vp_playfield->height;
9517 int new_dx = vp_door_1->x;
9518 int new_dy = vp_door_1->y;
9519 int new_dxsize = vp_door_1->width;
9520 int new_dysize = vp_door_1->height;
9521 int new_vx = vp_door_2->x;
9522 int new_vy = vp_door_2->y;
9523 int new_vxsize = vp_door_2->width;
9524 int new_vysize = vp_door_2->height;
9525 int new_ex = vp_door_3->x;
9526 int new_ey = vp_door_3->y;
9527 int new_exsize = vp_door_3->width;
9528 int new_eysize = vp_door_3->height;
9529 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9530 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9531 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9532 int new_scr_fieldx = new_sxsize / tilesize;
9533 int new_scr_fieldy = new_sysize / tilesize;
9534 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9535 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9536 boolean init_gfx_buffers = FALSE;
9537 boolean init_video_buffer = FALSE;
9538 boolean init_gadgets_and_anims = FALSE;
9539 boolean init_em_graphics = FALSE;
9541 if (new_win_xsize != WIN_XSIZE ||
9542 new_win_ysize != WIN_YSIZE)
9544 WIN_XSIZE = new_win_xsize;
9545 WIN_YSIZE = new_win_ysize;
9547 init_video_buffer = TRUE;
9548 init_gfx_buffers = TRUE;
9549 init_gadgets_and_anims = TRUE;
9551 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9554 if (new_scr_fieldx != SCR_FIELDX ||
9555 new_scr_fieldy != SCR_FIELDY)
9557 // this always toggles between MAIN and GAME when using small tile size
9559 SCR_FIELDX = new_scr_fieldx;
9560 SCR_FIELDY = new_scr_fieldy;
9562 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9573 new_sxsize != SXSIZE ||
9574 new_sysize != SYSIZE ||
9575 new_dxsize != DXSIZE ||
9576 new_dysize != DYSIZE ||
9577 new_vxsize != VXSIZE ||
9578 new_vysize != VYSIZE ||
9579 new_exsize != EXSIZE ||
9580 new_eysize != EYSIZE ||
9581 new_real_sx != REAL_SX ||
9582 new_real_sy != REAL_SY ||
9583 new_full_sxsize != FULL_SXSIZE ||
9584 new_full_sysize != FULL_SYSIZE ||
9585 new_tilesize_var != TILESIZE_VAR
9588 // ------------------------------------------------------------------------
9589 // determine next fading area for changed viewport definitions
9590 // ------------------------------------------------------------------------
9592 // start with current playfield area (default fading area)
9595 FADE_SXSIZE = FULL_SXSIZE;
9596 FADE_SYSIZE = FULL_SYSIZE;
9598 // add new playfield area if position or size has changed
9599 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9600 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9602 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9603 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9606 // add current and new door 1 area if position or size has changed
9607 if (new_dx != DX || new_dy != DY ||
9608 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9610 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9611 DX, DY, DXSIZE, DYSIZE);
9612 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9613 new_dx, new_dy, new_dxsize, new_dysize);
9616 // add current and new door 2 area if position or size has changed
9617 if (new_vx != VX || new_vy != VY ||
9618 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9620 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9621 VX, VY, VXSIZE, VYSIZE);
9622 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9623 new_vx, new_vy, new_vxsize, new_vysize);
9626 // ------------------------------------------------------------------------
9627 // handle changed tile size
9628 // ------------------------------------------------------------------------
9630 if (new_tilesize_var != TILESIZE_VAR)
9632 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9634 // changing tile size invalidates scroll values of engine snapshots
9635 FreeEngineSnapshotSingle();
9637 // changing tile size requires update of graphic mapping for EM engine
9638 init_em_graphics = TRUE;
9649 SXSIZE = new_sxsize;
9650 SYSIZE = new_sysize;
9651 DXSIZE = new_dxsize;
9652 DYSIZE = new_dysize;
9653 VXSIZE = new_vxsize;
9654 VYSIZE = new_vysize;
9655 EXSIZE = new_exsize;
9656 EYSIZE = new_eysize;
9657 REAL_SX = new_real_sx;
9658 REAL_SY = new_real_sy;
9659 FULL_SXSIZE = new_full_sxsize;
9660 FULL_SYSIZE = new_full_sysize;
9661 TILESIZE_VAR = new_tilesize_var;
9663 init_gfx_buffers = TRUE;
9664 init_gadgets_and_anims = TRUE;
9666 // printf("::: viewports: init_gfx_buffers\n");
9667 // printf("::: viewports: init_gadgets_and_anims\n");
9670 if (init_gfx_buffers)
9672 // printf("::: init_gfx_buffers\n");
9674 SCR_FIELDX = new_scr_fieldx_buffers;
9675 SCR_FIELDY = new_scr_fieldy_buffers;
9679 SCR_FIELDX = new_scr_fieldx;
9680 SCR_FIELDY = new_scr_fieldy;
9682 SetDrawDeactivationMask(REDRAW_NONE);
9683 SetDrawBackgroundMask(REDRAW_FIELD);
9686 if (init_video_buffer)
9688 // printf("::: init_video_buffer\n");
9690 FreeAllImageTextures(); // needs old renderer to free the textures
9692 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9693 InitImageTextures();
9696 if (init_gadgets_and_anims)
9698 // printf("::: init_gadgets_and_anims\n");
9701 InitGlobalAnimations();
9704 if (init_em_graphics)
9706 InitGraphicInfo_EM();