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(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 // this sets 'request_gadget_id'
4337 HandleGadgets(mx, my, button_status);
4339 switch (request_gadget_id)
4341 case TOOL_CTRL_ID_YES:
4344 case TOOL_CTRL_ID_NO:
4347 case TOOL_CTRL_ID_CONFIRM:
4348 result = TRUE | FALSE;
4351 case TOOL_CTRL_ID_PLAYER_1:
4354 case TOOL_CTRL_ID_PLAYER_2:
4357 case TOOL_CTRL_ID_PLAYER_3:
4360 case TOOL_CTRL_ID_PLAYER_4:
4371 case SDL_WINDOWEVENT:
4372 HandleWindowEvent((WindowEvent *) &event);
4375 case SDL_APP_WILLENTERBACKGROUND:
4376 case SDL_APP_DIDENTERBACKGROUND:
4377 case SDL_APP_WILLENTERFOREGROUND:
4378 case SDL_APP_DIDENTERFOREGROUND:
4379 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4382 case EVENT_KEYPRESS:
4384 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4389 if (req_state & REQ_CONFIRM)
4398 #if defined(KSYM_Rewind)
4399 case KSYM_Rewind: // for Amazon Fire TV remote
4408 #if defined(KSYM_FastForward)
4409 case KSYM_FastForward: // for Amazon Fire TV remote
4415 HandleKeysDebug(key, KEY_PRESSED);
4419 if (req_state & REQ_PLAYER)
4421 int old_player_nr = setup.network_player_nr;
4424 result = old_player_nr + 1;
4429 result = old_player_nr + 1;
4460 case EVENT_KEYRELEASE:
4461 ClearPlayerAction();
4464 case SDL_CONTROLLERBUTTONDOWN:
4465 switch (event.cbutton.button)
4467 case SDL_CONTROLLER_BUTTON_A:
4468 case SDL_CONTROLLER_BUTTON_X:
4469 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4470 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4474 case SDL_CONTROLLER_BUTTON_B:
4475 case SDL_CONTROLLER_BUTTON_Y:
4476 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4477 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4478 case SDL_CONTROLLER_BUTTON_BACK:
4483 if (req_state & REQ_PLAYER)
4485 int old_player_nr = setup.network_player_nr;
4488 result = old_player_nr + 1;
4490 switch (event.cbutton.button)
4492 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4493 case SDL_CONTROLLER_BUTTON_Y:
4497 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4498 case SDL_CONTROLLER_BUTTON_B:
4502 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4503 case SDL_CONTROLLER_BUTTON_A:
4507 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4508 case SDL_CONTROLLER_BUTTON_X:
4519 case SDL_CONTROLLERBUTTONUP:
4520 HandleJoystickEvent(&event);
4521 ClearPlayerAction();
4525 HandleOtherEvents(&event);
4530 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4532 int joy = AnyJoystick();
4534 if (joy & JOY_BUTTON_1)
4536 else if (joy & JOY_BUTTON_2)
4539 else if (AnyJoystick())
4541 int joy = AnyJoystick();
4543 if (req_state & REQ_PLAYER)
4547 else if (joy & JOY_RIGHT)
4549 else if (joy & JOY_DOWN)
4551 else if (joy & JOY_LEFT)
4556 if (game_just_ended)
4558 if (global.use_envelope_request)
4560 // copy back current state of pressed buttons inside request area
4561 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4568 game.request_active = FALSE;
4573 static boolean RequestDoor(char *text, unsigned int req_state)
4575 unsigned int old_door_state;
4576 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4577 int font_nr = FONT_TEXT_2;
4582 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4584 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4585 font_nr = FONT_TEXT_1;
4588 if (game_status == GAME_MODE_PLAYING)
4589 BlitScreenToBitmap(backbuffer);
4591 // disable deactivated drawing when quick-loading level tape recording
4592 if (tape.playing && tape.deactivate_display)
4593 TapeDeactivateDisplayOff(TRUE);
4595 SetMouseCursor(CURSOR_DEFAULT);
4597 // pause network game while waiting for request to answer
4598 if (network.enabled &&
4599 game_status == GAME_MODE_PLAYING &&
4600 !game.all_players_gone &&
4601 req_state & REQUEST_WAIT_FOR_INPUT)
4602 SendToServer_PausePlaying();
4604 old_door_state = GetDoorState();
4606 // simulate releasing mouse button over last gadget, if still pressed
4608 HandleGadgets(-1, -1, 0);
4612 // draw released gadget before proceeding
4615 if (old_door_state & DOOR_OPEN_1)
4617 CloseDoor(DOOR_CLOSE_1);
4619 // save old door content
4620 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4621 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4624 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4625 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4627 // clear door drawing field
4628 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4630 // force DOOR font inside door area
4631 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4633 // write text for request
4634 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4636 char text_line[max_request_line_len + 1];
4642 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4644 tc = *(text_ptr + tx);
4645 // if (!tc || tc == ' ')
4646 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4650 if ((tc == '?' || tc == '!') && tl == 0)
4660 strncpy(text_line, text_ptr, tl);
4663 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4664 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4665 text_line, font_nr);
4667 text_ptr += tl + (tc == ' ' ? 1 : 0);
4668 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4673 if (req_state & REQ_ASK)
4675 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4676 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4678 else if (req_state & REQ_CONFIRM)
4680 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4682 else if (req_state & REQ_PLAYER)
4684 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4685 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4686 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4687 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4690 // copy request gadgets to door backbuffer
4691 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4693 OpenDoor(DOOR_OPEN_1);
4695 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4697 if (game_status == GAME_MODE_PLAYING)
4699 SetPanelBackground();
4700 SetDrawBackgroundMask(REDRAW_DOOR_1);
4704 SetDrawBackgroundMask(REDRAW_FIELD);
4710 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4712 // ---------- handle request buttons ----------
4713 result = RequestHandleEvents(req_state);
4717 if (!(req_state & REQ_STAY_OPEN))
4719 CloseDoor(DOOR_CLOSE_1);
4721 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4722 (req_state & REQ_REOPEN))
4723 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4728 if (game_status == GAME_MODE_PLAYING)
4730 SetPanelBackground();
4731 SetDrawBackgroundMask(REDRAW_DOOR_1);
4735 SetDrawBackgroundMask(REDRAW_FIELD);
4738 // continue network game after request
4739 if (network.enabled &&
4740 game_status == GAME_MODE_PLAYING &&
4741 !game.all_players_gone &&
4742 req_state & REQUEST_WAIT_FOR_INPUT)
4743 SendToServer_ContinuePlaying();
4745 // restore deactivated drawing when quick-loading level tape recording
4746 if (tape.playing && tape.deactivate_display)
4747 TapeDeactivateDisplayOn();
4752 static boolean RequestEnvelope(char *text, unsigned int req_state)
4756 if (game_status == GAME_MODE_PLAYING)
4757 BlitScreenToBitmap(backbuffer);
4759 // disable deactivated drawing when quick-loading level tape recording
4760 if (tape.playing && tape.deactivate_display)
4761 TapeDeactivateDisplayOff(TRUE);
4763 SetMouseCursor(CURSOR_DEFAULT);
4765 // pause network game while waiting for request to answer
4766 if (network.enabled &&
4767 game_status == GAME_MODE_PLAYING &&
4768 !game.all_players_gone &&
4769 req_state & REQUEST_WAIT_FOR_INPUT)
4770 SendToServer_PausePlaying();
4772 // simulate releasing mouse button over last gadget, if still pressed
4774 HandleGadgets(-1, -1, 0);
4778 // (replace with setting corresponding request background)
4779 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4780 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4782 // clear door drawing field
4783 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4785 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4787 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4789 if (game_status == GAME_MODE_PLAYING)
4791 SetPanelBackground();
4792 SetDrawBackgroundMask(REDRAW_DOOR_1);
4796 SetDrawBackgroundMask(REDRAW_FIELD);
4802 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4804 // ---------- handle request buttons ----------
4805 result = RequestHandleEvents(req_state);
4809 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4813 if (game_status == GAME_MODE_PLAYING)
4815 SetPanelBackground();
4816 SetDrawBackgroundMask(REDRAW_DOOR_1);
4820 SetDrawBackgroundMask(REDRAW_FIELD);
4823 // continue network game after request
4824 if (network.enabled &&
4825 game_status == GAME_MODE_PLAYING &&
4826 !game.all_players_gone &&
4827 req_state & REQUEST_WAIT_FOR_INPUT)
4828 SendToServer_ContinuePlaying();
4830 // restore deactivated drawing when quick-loading level tape recording
4831 if (tape.playing && tape.deactivate_display)
4832 TapeDeactivateDisplayOn();
4837 boolean Request(char *text, unsigned int req_state)
4839 boolean overlay_enabled = GetOverlayEnabled();
4842 SetOverlayEnabled(FALSE);
4844 if (global.use_envelope_request)
4845 result = RequestEnvelope(text, req_state);
4847 result = RequestDoor(text, req_state);
4849 SetOverlayEnabled(overlay_enabled);
4854 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4856 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4857 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4860 if (dpo1->sort_priority != dpo2->sort_priority)
4861 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4863 compare_result = dpo1->nr - dpo2->nr;
4865 return compare_result;
4868 void InitGraphicCompatibilityInfo_Doors(void)
4874 struct DoorInfo *door;
4878 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4879 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4881 { -1, -1, -1, NULL }
4883 struct Rect door_rect_list[] =
4885 { DX, DY, DXSIZE, DYSIZE },
4886 { VX, VY, VXSIZE, VYSIZE }
4890 for (i = 0; doors[i].door_token != -1; i++)
4892 int door_token = doors[i].door_token;
4893 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4894 int part_1 = doors[i].part_1;
4895 int part_8 = doors[i].part_8;
4896 int part_2 = part_1 + 1;
4897 int part_3 = part_1 + 2;
4898 struct DoorInfo *door = doors[i].door;
4899 struct Rect *door_rect = &door_rect_list[door_index];
4900 boolean door_gfx_redefined = FALSE;
4902 // check if any door part graphic definitions have been redefined
4904 for (j = 0; door_part_controls[j].door_token != -1; j++)
4906 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4907 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4909 if (dpc->door_token == door_token && fi->redefined)
4910 door_gfx_redefined = TRUE;
4913 // check for old-style door graphic/animation modifications
4915 if (!door_gfx_redefined)
4917 if (door->anim_mode & ANIM_STATIC_PANEL)
4919 door->panel.step_xoffset = 0;
4920 door->panel.step_yoffset = 0;
4923 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4925 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4926 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4927 int num_door_steps, num_panel_steps;
4929 // remove door part graphics other than the two default wings
4931 for (j = 0; door_part_controls[j].door_token != -1; j++)
4933 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4934 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4936 if (dpc->graphic >= part_3 &&
4937 dpc->graphic <= part_8)
4941 // set graphics and screen positions of the default wings
4943 g_part_1->width = door_rect->width;
4944 g_part_1->height = door_rect->height;
4945 g_part_2->width = door_rect->width;
4946 g_part_2->height = door_rect->height;
4947 g_part_2->src_x = door_rect->width;
4948 g_part_2->src_y = g_part_1->src_y;
4950 door->part_2.x = door->part_1.x;
4951 door->part_2.y = door->part_1.y;
4953 if (door->width != -1)
4955 g_part_1->width = door->width;
4956 g_part_2->width = door->width;
4958 // special treatment for graphics and screen position of right wing
4959 g_part_2->src_x += door_rect->width - door->width;
4960 door->part_2.x += door_rect->width - door->width;
4963 if (door->height != -1)
4965 g_part_1->height = door->height;
4966 g_part_2->height = door->height;
4968 // special treatment for graphics and screen position of bottom wing
4969 g_part_2->src_y += door_rect->height - door->height;
4970 door->part_2.y += door_rect->height - door->height;
4973 // set animation delays for the default wings and panels
4975 door->part_1.step_delay = door->step_delay;
4976 door->part_2.step_delay = door->step_delay;
4977 door->panel.step_delay = door->step_delay;
4979 // set animation draw order for the default wings
4981 door->part_1.sort_priority = 2; // draw left wing over ...
4982 door->part_2.sort_priority = 1; // ... right wing
4984 // set animation draw offset for the default wings
4986 if (door->anim_mode & ANIM_HORIZONTAL)
4988 door->part_1.step_xoffset = door->step_offset;
4989 door->part_1.step_yoffset = 0;
4990 door->part_2.step_xoffset = door->step_offset * -1;
4991 door->part_2.step_yoffset = 0;
4993 num_door_steps = g_part_1->width / door->step_offset;
4995 else // ANIM_VERTICAL
4997 door->part_1.step_xoffset = 0;
4998 door->part_1.step_yoffset = door->step_offset;
4999 door->part_2.step_xoffset = 0;
5000 door->part_2.step_yoffset = door->step_offset * -1;
5002 num_door_steps = g_part_1->height / door->step_offset;
5005 // set animation draw offset for the default panels
5007 if (door->step_offset > 1)
5009 num_panel_steps = 2 * door_rect->height / door->step_offset;
5010 door->panel.start_step = num_panel_steps - num_door_steps;
5011 door->panel.start_step_closing = door->panel.start_step;
5015 num_panel_steps = door_rect->height / door->step_offset;
5016 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5017 door->panel.start_step_closing = door->panel.start_step;
5018 door->panel.step_delay *= 2;
5025 void InitDoors(void)
5029 for (i = 0; door_part_controls[i].door_token != -1; i++)
5031 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5032 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5034 // initialize "start_step_opening" and "start_step_closing", if needed
5035 if (dpc->pos->start_step_opening == 0 &&
5036 dpc->pos->start_step_closing == 0)
5038 // dpc->pos->start_step_opening = dpc->pos->start_step;
5039 dpc->pos->start_step_closing = dpc->pos->start_step;
5042 // fill structure for door part draw order (sorted below)
5044 dpo->sort_priority = dpc->pos->sort_priority;
5047 // sort door part controls according to sort_priority and graphic number
5048 qsort(door_part_order, MAX_DOOR_PARTS,
5049 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5052 unsigned int OpenDoor(unsigned int door_state)
5054 if (door_state & DOOR_COPY_BACK)
5056 if (door_state & DOOR_OPEN_1)
5057 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5058 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5060 if (door_state & DOOR_OPEN_2)
5061 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5062 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5064 door_state &= ~DOOR_COPY_BACK;
5067 return MoveDoor(door_state);
5070 unsigned int CloseDoor(unsigned int door_state)
5072 unsigned int old_door_state = GetDoorState();
5074 if (!(door_state & DOOR_NO_COPY_BACK))
5076 if (old_door_state & DOOR_OPEN_1)
5077 BlitBitmap(backbuffer, bitmap_db_door_1,
5078 DX, DY, DXSIZE, DYSIZE, 0, 0);
5080 if (old_door_state & DOOR_OPEN_2)
5081 BlitBitmap(backbuffer, bitmap_db_door_2,
5082 VX, VY, VXSIZE, VYSIZE, 0, 0);
5084 door_state &= ~DOOR_NO_COPY_BACK;
5087 return MoveDoor(door_state);
5090 unsigned int GetDoorState(void)
5092 return MoveDoor(DOOR_GET_STATE);
5095 unsigned int SetDoorState(unsigned int door_state)
5097 return MoveDoor(door_state | DOOR_SET_STATE);
5100 static int euclid(int a, int b)
5102 return (b ? euclid(b, a % b) : a);
5105 unsigned int MoveDoor(unsigned int door_state)
5107 struct Rect door_rect_list[] =
5109 { DX, DY, DXSIZE, DYSIZE },
5110 { VX, VY, VXSIZE, VYSIZE }
5112 static int door1 = DOOR_CLOSE_1;
5113 static int door2 = DOOR_CLOSE_2;
5114 unsigned int door_delay = 0;
5115 unsigned int door_delay_value;
5118 if (door_state == DOOR_GET_STATE)
5119 return (door1 | door2);
5121 if (door_state & DOOR_SET_STATE)
5123 if (door_state & DOOR_ACTION_1)
5124 door1 = door_state & DOOR_ACTION_1;
5125 if (door_state & DOOR_ACTION_2)
5126 door2 = door_state & DOOR_ACTION_2;
5128 return (door1 | door2);
5131 if (!(door_state & DOOR_FORCE_REDRAW))
5133 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5134 door_state &= ~DOOR_OPEN_1;
5135 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5136 door_state &= ~DOOR_CLOSE_1;
5137 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5138 door_state &= ~DOOR_OPEN_2;
5139 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5140 door_state &= ~DOOR_CLOSE_2;
5143 if (global.autoplay_leveldir)
5145 door_state |= DOOR_NO_DELAY;
5146 door_state &= ~DOOR_CLOSE_ALL;
5149 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5150 door_state |= DOOR_NO_DELAY;
5152 if (door_state & DOOR_ACTION)
5154 boolean door_panel_drawn[NUM_DOORS];
5155 boolean panel_has_doors[NUM_DOORS];
5156 boolean door_part_skip[MAX_DOOR_PARTS];
5157 boolean door_part_done[MAX_DOOR_PARTS];
5158 boolean door_part_done_all;
5159 int num_steps[MAX_DOOR_PARTS];
5160 int max_move_delay = 0; // delay for complete animations of all doors
5161 int max_step_delay = 0; // delay (ms) between two animation frames
5162 int num_move_steps = 0; // number of animation steps for all doors
5163 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5164 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5165 int current_move_delay = 0;
5169 for (i = 0; i < NUM_DOORS; i++)
5170 panel_has_doors[i] = FALSE;
5172 for (i = 0; i < MAX_DOOR_PARTS; i++)
5174 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5175 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5176 int door_token = dpc->door_token;
5178 door_part_done[i] = FALSE;
5179 door_part_skip[i] = (!(door_state & door_token) ||
5183 for (i = 0; i < MAX_DOOR_PARTS; i++)
5185 int nr = door_part_order[i].nr;
5186 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5187 struct DoorPartPosInfo *pos = dpc->pos;
5188 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5189 int door_token = dpc->door_token;
5190 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5191 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5192 int step_xoffset = ABS(pos->step_xoffset);
5193 int step_yoffset = ABS(pos->step_yoffset);
5194 int step_delay = pos->step_delay;
5195 int current_door_state = door_state & door_token;
5196 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5197 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5198 boolean part_opening = (is_panel ? door_closing : door_opening);
5199 int start_step = (part_opening ? pos->start_step_opening :
5200 pos->start_step_closing);
5201 float move_xsize = (step_xoffset ? g->width : 0);
5202 float move_ysize = (step_yoffset ? g->height : 0);
5203 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5204 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5205 int move_steps = (move_xsteps && move_ysteps ?
5206 MIN(move_xsteps, move_ysteps) :
5207 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5208 int move_delay = move_steps * step_delay;
5210 if (door_part_skip[nr])
5213 max_move_delay = MAX(max_move_delay, move_delay);
5214 max_step_delay = (max_step_delay == 0 ? step_delay :
5215 euclid(max_step_delay, step_delay));
5216 num_steps[nr] = move_steps;
5220 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5222 panel_has_doors[door_index] = TRUE;
5226 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5228 num_move_steps = max_move_delay / max_step_delay;
5229 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5231 door_delay_value = max_step_delay;
5233 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5235 start = num_move_steps - 1;
5239 // opening door sound has priority over simultaneously closing door
5240 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5242 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5244 if (door_state & DOOR_OPEN_1)
5245 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5246 if (door_state & DOOR_OPEN_2)
5247 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5249 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5251 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5253 if (door_state & DOOR_CLOSE_1)
5254 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5255 if (door_state & DOOR_CLOSE_2)
5256 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5260 for (k = start; k < num_move_steps; k++)
5262 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5264 door_part_done_all = TRUE;
5266 for (i = 0; i < NUM_DOORS; i++)
5267 door_panel_drawn[i] = FALSE;
5269 for (i = 0; i < MAX_DOOR_PARTS; i++)
5271 int nr = door_part_order[i].nr;
5272 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5273 struct DoorPartPosInfo *pos = dpc->pos;
5274 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5275 int door_token = dpc->door_token;
5276 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5277 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5278 boolean is_panel_and_door_has_closed = FALSE;
5279 struct Rect *door_rect = &door_rect_list[door_index];
5280 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5282 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5283 int current_door_state = door_state & door_token;
5284 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5285 boolean door_closing = !door_opening;
5286 boolean part_opening = (is_panel ? door_closing : door_opening);
5287 boolean part_closing = !part_opening;
5288 int start_step = (part_opening ? pos->start_step_opening :
5289 pos->start_step_closing);
5290 int step_delay = pos->step_delay;
5291 int step_factor = step_delay / max_step_delay;
5292 int k1 = (step_factor ? k / step_factor + 1 : k);
5293 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5294 int kk = MAX(0, k2);
5297 int src_x, src_y, src_xx, src_yy;
5298 int dst_x, dst_y, dst_xx, dst_yy;
5301 if (door_part_skip[nr])
5304 if (!(door_state & door_token))
5312 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5313 int kk_door = MAX(0, k2_door);
5314 int sync_frame = kk_door * door_delay_value;
5315 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5317 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5318 &g_src_x, &g_src_y);
5323 if (!door_panel_drawn[door_index])
5325 ClearRectangle(drawto, door_rect->x, door_rect->y,
5326 door_rect->width, door_rect->height);
5328 door_panel_drawn[door_index] = TRUE;
5331 // draw opening or closing door parts
5333 if (pos->step_xoffset < 0) // door part on right side
5336 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5339 if (dst_xx + width > door_rect->width)
5340 width = door_rect->width - dst_xx;
5342 else // door part on left side
5345 dst_xx = pos->x - kk * pos->step_xoffset;
5349 src_xx = ABS(dst_xx);
5353 width = g->width - src_xx;
5355 if (width > door_rect->width)
5356 width = door_rect->width;
5358 // printf("::: k == %d [%d] \n", k, start_step);
5361 if (pos->step_yoffset < 0) // door part on bottom side
5364 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5367 if (dst_yy + height > door_rect->height)
5368 height = door_rect->height - dst_yy;
5370 else // door part on top side
5373 dst_yy = pos->y - kk * pos->step_yoffset;
5377 src_yy = ABS(dst_yy);
5381 height = g->height - src_yy;
5384 src_x = g_src_x + src_xx;
5385 src_y = g_src_y + src_yy;
5387 dst_x = door_rect->x + dst_xx;
5388 dst_y = door_rect->y + dst_yy;
5390 is_panel_and_door_has_closed =
5393 panel_has_doors[door_index] &&
5394 k >= num_move_steps_doors_only - 1);
5396 if (width >= 0 && width <= g->width &&
5397 height >= 0 && height <= g->height &&
5398 !is_panel_and_door_has_closed)
5400 if (is_panel || !pos->draw_masked)
5401 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5404 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5408 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5410 if ((part_opening && (width < 0 || height < 0)) ||
5411 (part_closing && (width >= g->width && height >= g->height)))
5412 door_part_done[nr] = TRUE;
5414 // continue door part animations, but not panel after door has closed
5415 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5416 door_part_done_all = FALSE;
5419 if (!(door_state & DOOR_NO_DELAY))
5423 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5425 current_move_delay += max_step_delay;
5427 // prevent OS (Windows) from complaining about program not responding
5431 if (door_part_done_all)
5435 if (!(door_state & DOOR_NO_DELAY))
5437 // wait for specified door action post delay
5438 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5439 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5440 else if (door_state & DOOR_ACTION_1)
5441 door_delay_value = door_1.post_delay;
5442 else if (door_state & DOOR_ACTION_2)
5443 door_delay_value = door_2.post_delay;
5445 while (!DelayReached(&door_delay, door_delay_value))
5450 if (door_state & DOOR_ACTION_1)
5451 door1 = door_state & DOOR_ACTION_1;
5452 if (door_state & DOOR_ACTION_2)
5453 door2 = door_state & DOOR_ACTION_2;
5455 // draw masked border over door area
5456 DrawMaskedBorder(REDRAW_DOOR_1);
5457 DrawMaskedBorder(REDRAW_DOOR_2);
5459 ClearAutoRepeatKeyEvents();
5461 return (door1 | door2);
5464 static boolean useSpecialEditorDoor(void)
5466 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5467 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5469 // do not draw special editor door if editor border defined or redefined
5470 if (graphic_info[graphic].bitmap != NULL || redefined)
5473 // do not draw special editor door if global border defined to be empty
5474 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5477 // do not draw special editor door if viewport definitions do not match
5481 EY + EYSIZE != VY + VYSIZE)
5487 void DrawSpecialEditorDoor(void)
5489 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5490 int top_border_width = gfx1->width;
5491 int top_border_height = gfx1->height;
5492 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5493 int ex = EX - outer_border;
5494 int ey = EY - outer_border;
5495 int vy = VY - outer_border;
5496 int exsize = EXSIZE + 2 * outer_border;
5498 if (!useSpecialEditorDoor())
5501 // draw bigger level editor toolbox window
5502 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5503 top_border_width, top_border_height, ex, ey - top_border_height);
5504 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5505 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5507 redraw_mask |= REDRAW_ALL;
5510 void UndrawSpecialEditorDoor(void)
5512 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5513 int top_border_width = gfx1->width;
5514 int top_border_height = gfx1->height;
5515 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5516 int ex = EX - outer_border;
5517 int ey = EY - outer_border;
5518 int ey_top = ey - top_border_height;
5519 int exsize = EXSIZE + 2 * outer_border;
5520 int eysize = EYSIZE + 2 * outer_border;
5522 if (!useSpecialEditorDoor())
5525 // draw normal tape recorder window
5526 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5528 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5529 ex, ey_top, top_border_width, top_border_height,
5531 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5532 ex, ey, exsize, eysize, ex, ey);
5536 // if screen background is set to "[NONE]", clear editor toolbox window
5537 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5538 ClearRectangle(drawto, ex, ey, exsize, eysize);
5541 redraw_mask |= REDRAW_ALL;
5545 // ---------- new tool button stuff -------------------------------------------
5550 struct TextPosInfo *pos;
5553 } toolbutton_info[NUM_TOOL_BUTTONS] =
5556 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5557 TOOL_CTRL_ID_YES, "yes"
5560 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5561 TOOL_CTRL_ID_NO, "no"
5564 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5565 TOOL_CTRL_ID_CONFIRM, "confirm"
5568 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5569 TOOL_CTRL_ID_PLAYER_1, "player 1"
5572 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5573 TOOL_CTRL_ID_PLAYER_2, "player 2"
5576 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5577 TOOL_CTRL_ID_PLAYER_3, "player 3"
5580 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5581 TOOL_CTRL_ID_PLAYER_4, "player 4"
5585 void CreateToolButtons(void)
5589 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5591 int graphic = toolbutton_info[i].graphic;
5592 struct GraphicInfo *gfx = &graphic_info[graphic];
5593 struct TextPosInfo *pos = toolbutton_info[i].pos;
5594 struct GadgetInfo *gi;
5595 Bitmap *deco_bitmap = None;
5596 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5597 unsigned int event_mask = GD_EVENT_RELEASED;
5600 int gd_x = gfx->src_x;
5601 int gd_y = gfx->src_y;
5602 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5603 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5608 if (global.use_envelope_request)
5610 setRequestPosition(&dx, &dy, TRUE);
5612 // check if request buttons are outside of envelope and fix, if needed
5613 if (x < 0 || x + gfx->width > request.width ||
5614 y < 0 || y + gfx->height > request.height)
5616 if (id == TOOL_CTRL_ID_YES)
5619 y = request.height - 2 * request.border_size - gfx->height;
5621 else if (id == TOOL_CTRL_ID_NO)
5623 x = request.width - 2 * request.border_size - gfx->width;
5624 y = request.height - 2 * request.border_size - gfx->height;
5626 else if (id == TOOL_CTRL_ID_CONFIRM)
5628 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5629 y = request.height - 2 * request.border_size - gfx->height;
5631 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5633 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5635 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5636 y = request.height - 2 * request.border_size - gfx->height * 2;
5638 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5639 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5644 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5646 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5648 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5649 pos->size, &deco_bitmap, &deco_x, &deco_y);
5650 deco_xpos = (gfx->width - pos->size) / 2;
5651 deco_ypos = (gfx->height - pos->size) / 2;
5654 gi = CreateGadget(GDI_CUSTOM_ID, id,
5655 GDI_IMAGE_ID, graphic,
5656 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5659 GDI_WIDTH, gfx->width,
5660 GDI_HEIGHT, gfx->height,
5661 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5662 GDI_STATE, GD_BUTTON_UNPRESSED,
5663 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5664 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5665 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5666 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5667 GDI_DECORATION_SIZE, pos->size, pos->size,
5668 GDI_DECORATION_SHIFTING, 1, 1,
5669 GDI_DIRECT_DRAW, FALSE,
5670 GDI_EVENT_MASK, event_mask,
5671 GDI_CALLBACK_ACTION, HandleToolButtons,
5675 Error(ERR_EXIT, "cannot create gadget");
5677 tool_gadget[id] = gi;
5681 void FreeToolButtons(void)
5685 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5686 FreeGadget(tool_gadget[i]);
5689 static void UnmapToolButtons(void)
5693 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5694 UnmapGadget(tool_gadget[i]);
5697 static void HandleToolButtons(struct GadgetInfo *gi)
5699 request_gadget_id = gi->custom_id;
5702 static struct Mapping_EM_to_RND_object
5705 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5706 boolean is_backside; // backside of moving element
5712 em_object_mapping_list[] =
5715 Xblank, TRUE, FALSE,
5719 Yacid_splash_eB, FALSE, FALSE,
5720 EL_ACID_SPLASH_RIGHT, -1, -1
5723 Yacid_splash_wB, FALSE, FALSE,
5724 EL_ACID_SPLASH_LEFT, -1, -1
5727 #ifdef EM_ENGINE_BAD_ROLL
5729 Xstone_force_e, FALSE, FALSE,
5730 EL_ROCK, -1, MV_BIT_RIGHT
5733 Xstone_force_w, FALSE, FALSE,
5734 EL_ROCK, -1, MV_BIT_LEFT
5737 Xnut_force_e, FALSE, FALSE,
5738 EL_NUT, -1, MV_BIT_RIGHT
5741 Xnut_force_w, FALSE, FALSE,
5742 EL_NUT, -1, MV_BIT_LEFT
5745 Xspring_force_e, FALSE, FALSE,
5746 EL_SPRING, -1, MV_BIT_RIGHT
5749 Xspring_force_w, FALSE, FALSE,
5750 EL_SPRING, -1, MV_BIT_LEFT
5753 Xemerald_force_e, FALSE, FALSE,
5754 EL_EMERALD, -1, MV_BIT_RIGHT
5757 Xemerald_force_w, FALSE, FALSE,
5758 EL_EMERALD, -1, MV_BIT_LEFT
5761 Xdiamond_force_e, FALSE, FALSE,
5762 EL_DIAMOND, -1, MV_BIT_RIGHT
5765 Xdiamond_force_w, FALSE, FALSE,
5766 EL_DIAMOND, -1, MV_BIT_LEFT
5769 Xbomb_force_e, FALSE, FALSE,
5770 EL_BOMB, -1, MV_BIT_RIGHT
5773 Xbomb_force_w, FALSE, FALSE,
5774 EL_BOMB, -1, MV_BIT_LEFT
5776 #endif // EM_ENGINE_BAD_ROLL
5779 Xstone, TRUE, FALSE,
5783 Xstone_pause, FALSE, FALSE,
5787 Xstone_fall, FALSE, FALSE,
5791 Ystone_s, FALSE, FALSE,
5792 EL_ROCK, ACTION_FALLING, -1
5795 Ystone_sB, FALSE, TRUE,
5796 EL_ROCK, ACTION_FALLING, -1
5799 Ystone_e, FALSE, FALSE,
5800 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5803 Ystone_eB, FALSE, TRUE,
5804 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5807 Ystone_w, FALSE, FALSE,
5808 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5811 Ystone_wB, FALSE, TRUE,
5812 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5819 Xnut_pause, FALSE, FALSE,
5823 Xnut_fall, FALSE, FALSE,
5827 Ynut_s, FALSE, FALSE,
5828 EL_NUT, ACTION_FALLING, -1
5831 Ynut_sB, FALSE, TRUE,
5832 EL_NUT, ACTION_FALLING, -1
5835 Ynut_e, FALSE, FALSE,
5836 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5839 Ynut_eB, FALSE, TRUE,
5840 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5843 Ynut_w, FALSE, FALSE,
5844 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5847 Ynut_wB, FALSE, TRUE,
5848 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5851 Xbug_n, TRUE, FALSE,
5855 Xbug_e, TRUE, FALSE,
5856 EL_BUG_RIGHT, -1, -1
5859 Xbug_s, TRUE, FALSE,
5863 Xbug_w, TRUE, FALSE,
5867 Xbug_gon, FALSE, FALSE,
5871 Xbug_goe, FALSE, FALSE,
5872 EL_BUG_RIGHT, -1, -1
5875 Xbug_gos, FALSE, FALSE,
5879 Xbug_gow, FALSE, FALSE,
5883 Ybug_n, FALSE, FALSE,
5884 EL_BUG, ACTION_MOVING, MV_BIT_UP
5887 Ybug_nB, FALSE, TRUE,
5888 EL_BUG, ACTION_MOVING, MV_BIT_UP
5891 Ybug_e, FALSE, FALSE,
5892 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5895 Ybug_eB, FALSE, TRUE,
5896 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5899 Ybug_s, FALSE, FALSE,
5900 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5903 Ybug_sB, FALSE, TRUE,
5904 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5907 Ybug_w, FALSE, FALSE,
5908 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5911 Ybug_wB, FALSE, TRUE,
5912 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5915 Ybug_w_n, FALSE, FALSE,
5916 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5919 Ybug_n_e, FALSE, FALSE,
5920 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5923 Ybug_e_s, FALSE, FALSE,
5924 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5927 Ybug_s_w, FALSE, FALSE,
5928 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5931 Ybug_e_n, FALSE, FALSE,
5932 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5935 Ybug_s_e, FALSE, FALSE,
5936 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5939 Ybug_w_s, FALSE, FALSE,
5940 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5943 Ybug_n_w, FALSE, FALSE,
5944 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5947 Ybug_stone, FALSE, FALSE,
5948 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5951 Ybug_spring, FALSE, FALSE,
5952 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5955 Xtank_n, TRUE, FALSE,
5956 EL_SPACESHIP_UP, -1, -1
5959 Xtank_e, TRUE, FALSE,
5960 EL_SPACESHIP_RIGHT, -1, -1
5963 Xtank_s, TRUE, FALSE,
5964 EL_SPACESHIP_DOWN, -1, -1
5967 Xtank_w, TRUE, FALSE,
5968 EL_SPACESHIP_LEFT, -1, -1
5971 Xtank_gon, FALSE, FALSE,
5972 EL_SPACESHIP_UP, -1, -1
5975 Xtank_goe, FALSE, FALSE,
5976 EL_SPACESHIP_RIGHT, -1, -1
5979 Xtank_gos, FALSE, FALSE,
5980 EL_SPACESHIP_DOWN, -1, -1
5983 Xtank_gow, FALSE, FALSE,
5984 EL_SPACESHIP_LEFT, -1, -1
5987 Ytank_n, FALSE, FALSE,
5988 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5991 Ytank_nB, FALSE, TRUE,
5992 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5995 Ytank_e, FALSE, FALSE,
5996 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5999 Ytank_eB, FALSE, TRUE,
6000 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6003 Ytank_s, FALSE, FALSE,
6004 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6007 Ytank_sB, FALSE, TRUE,
6008 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6011 Ytank_w, FALSE, FALSE,
6012 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6015 Ytank_wB, FALSE, TRUE,
6016 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6019 Ytank_w_n, FALSE, FALSE,
6020 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6023 Ytank_n_e, FALSE, FALSE,
6024 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6027 Ytank_e_s, FALSE, FALSE,
6028 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6031 Ytank_s_w, FALSE, FALSE,
6032 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6035 Ytank_e_n, FALSE, FALSE,
6036 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6039 Ytank_s_e, FALSE, FALSE,
6040 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6043 Ytank_w_s, FALSE, FALSE,
6044 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6047 Ytank_n_w, FALSE, FALSE,
6048 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6051 Ytank_stone, FALSE, FALSE,
6052 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6055 Ytank_spring, FALSE, FALSE,
6056 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6059 Xandroid, TRUE, FALSE,
6060 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6063 Xandroid_1_n, FALSE, FALSE,
6064 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6067 Xandroid_2_n, FALSE, FALSE,
6068 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6071 Xandroid_1_e, FALSE, FALSE,
6072 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6075 Xandroid_2_e, FALSE, FALSE,
6076 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6079 Xandroid_1_w, FALSE, FALSE,
6080 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6083 Xandroid_2_w, FALSE, FALSE,
6084 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6087 Xandroid_1_s, FALSE, FALSE,
6088 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6091 Xandroid_2_s, FALSE, FALSE,
6092 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6095 Yandroid_n, FALSE, FALSE,
6096 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6099 Yandroid_nB, FALSE, TRUE,
6100 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6103 Yandroid_ne, FALSE, FALSE,
6104 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6107 Yandroid_neB, FALSE, TRUE,
6108 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6111 Yandroid_e, FALSE, FALSE,
6112 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6115 Yandroid_eB, FALSE, TRUE,
6116 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6119 Yandroid_se, FALSE, FALSE,
6120 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6123 Yandroid_seB, FALSE, TRUE,
6124 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6127 Yandroid_s, FALSE, FALSE,
6128 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6131 Yandroid_sB, FALSE, TRUE,
6132 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6135 Yandroid_sw, FALSE, FALSE,
6136 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6139 Yandroid_swB, FALSE, TRUE,
6140 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6143 Yandroid_w, FALSE, FALSE,
6144 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6147 Yandroid_wB, FALSE, TRUE,
6148 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6151 Yandroid_nw, FALSE, FALSE,
6152 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6155 Yandroid_nwB, FALSE, TRUE,
6156 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6159 Xspring, TRUE, FALSE,
6163 Xspring_pause, FALSE, FALSE,
6167 Xspring_e, FALSE, FALSE,
6171 Xspring_w, FALSE, FALSE,
6175 Xspring_fall, FALSE, FALSE,
6179 Yspring_s, FALSE, FALSE,
6180 EL_SPRING, ACTION_FALLING, -1
6183 Yspring_sB, FALSE, TRUE,
6184 EL_SPRING, ACTION_FALLING, -1
6187 Yspring_e, FALSE, FALSE,
6188 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6191 Yspring_eB, FALSE, TRUE,
6192 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6195 Yspring_w, FALSE, FALSE,
6196 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6199 Yspring_wB, FALSE, TRUE,
6200 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6203 Yspring_kill_e, FALSE, FALSE,
6204 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6207 Yspring_kill_eB, FALSE, TRUE,
6208 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6211 Yspring_kill_w, FALSE, FALSE,
6212 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6215 Yspring_kill_wB, FALSE, TRUE,
6216 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6219 Xeater_n, TRUE, FALSE,
6220 EL_YAMYAM_UP, -1, -1
6223 Xeater_e, TRUE, FALSE,
6224 EL_YAMYAM_RIGHT, -1, -1
6227 Xeater_w, TRUE, FALSE,
6228 EL_YAMYAM_LEFT, -1, -1
6231 Xeater_s, TRUE, FALSE,
6232 EL_YAMYAM_DOWN, -1, -1
6235 Yeater_n, FALSE, FALSE,
6236 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6239 Yeater_nB, FALSE, TRUE,
6240 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6243 Yeater_e, FALSE, FALSE,
6244 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6247 Yeater_eB, FALSE, TRUE,
6248 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6251 Yeater_s, FALSE, FALSE,
6252 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6255 Yeater_sB, FALSE, TRUE,
6256 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6259 Yeater_w, FALSE, FALSE,
6260 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6263 Yeater_wB, FALSE, TRUE,
6264 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6267 Yeater_stone, FALSE, FALSE,
6268 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6271 Yeater_spring, FALSE, FALSE,
6272 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6275 Xalien, TRUE, FALSE,
6279 Xalien_pause, FALSE, FALSE,
6283 Yalien_n, FALSE, FALSE,
6284 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6287 Yalien_nB, FALSE, TRUE,
6288 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6291 Yalien_e, FALSE, FALSE,
6292 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6295 Yalien_eB, FALSE, TRUE,
6296 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6299 Yalien_s, FALSE, FALSE,
6300 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6303 Yalien_sB, FALSE, TRUE,
6304 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6307 Yalien_w, FALSE, FALSE,
6308 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6311 Yalien_wB, FALSE, TRUE,
6312 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6315 Yalien_stone, FALSE, FALSE,
6316 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6319 Yalien_spring, FALSE, FALSE,
6320 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6323 Xemerald, TRUE, FALSE,
6327 Xemerald_pause, FALSE, FALSE,
6331 Xemerald_fall, FALSE, FALSE,
6335 Xemerald_shine, FALSE, FALSE,
6336 EL_EMERALD, ACTION_TWINKLING, -1
6339 Yemerald_s, FALSE, FALSE,
6340 EL_EMERALD, ACTION_FALLING, -1
6343 Yemerald_sB, FALSE, TRUE,
6344 EL_EMERALD, ACTION_FALLING, -1
6347 Yemerald_e, FALSE, FALSE,
6348 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6351 Yemerald_eB, FALSE, TRUE,
6352 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6355 Yemerald_w, FALSE, FALSE,
6356 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6359 Yemerald_wB, FALSE, TRUE,
6360 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6363 Yemerald_eat, FALSE, FALSE,
6364 EL_EMERALD, ACTION_COLLECTING, -1
6367 Yemerald_stone, FALSE, FALSE,
6368 EL_NUT, ACTION_BREAKING, -1
6371 Xdiamond, TRUE, FALSE,
6375 Xdiamond_pause, FALSE, FALSE,
6379 Xdiamond_fall, FALSE, FALSE,
6383 Xdiamond_shine, FALSE, FALSE,
6384 EL_DIAMOND, ACTION_TWINKLING, -1
6387 Ydiamond_s, FALSE, FALSE,
6388 EL_DIAMOND, ACTION_FALLING, -1
6391 Ydiamond_sB, FALSE, TRUE,
6392 EL_DIAMOND, ACTION_FALLING, -1
6395 Ydiamond_e, FALSE, FALSE,
6396 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6399 Ydiamond_eB, FALSE, TRUE,
6400 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6403 Ydiamond_w, FALSE, FALSE,
6404 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6407 Ydiamond_wB, FALSE, TRUE,
6408 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6411 Ydiamond_eat, FALSE, FALSE,
6412 EL_DIAMOND, ACTION_COLLECTING, -1
6415 Ydiamond_stone, FALSE, FALSE,
6416 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6419 Xdrip_fall, TRUE, FALSE,
6420 EL_AMOEBA_DROP, -1, -1
6423 Xdrip_stretch, FALSE, FALSE,
6424 EL_AMOEBA_DROP, ACTION_FALLING, -1
6427 Xdrip_stretchB, FALSE, TRUE,
6428 EL_AMOEBA_DROP, ACTION_FALLING, -1
6431 Xdrip_eat, FALSE, FALSE,
6432 EL_AMOEBA_DROP, ACTION_GROWING, -1
6435 Ydrip_s1, FALSE, FALSE,
6436 EL_AMOEBA_DROP, ACTION_FALLING, -1
6439 Ydrip_s1B, FALSE, TRUE,
6440 EL_AMOEBA_DROP, ACTION_FALLING, -1
6443 Ydrip_s2, FALSE, FALSE,
6444 EL_AMOEBA_DROP, ACTION_FALLING, -1
6447 Ydrip_s2B, FALSE, TRUE,
6448 EL_AMOEBA_DROP, ACTION_FALLING, -1
6455 Xbomb_pause, FALSE, FALSE,
6459 Xbomb_fall, FALSE, FALSE,
6463 Ybomb_s, FALSE, FALSE,
6464 EL_BOMB, ACTION_FALLING, -1
6467 Ybomb_sB, FALSE, TRUE,
6468 EL_BOMB, ACTION_FALLING, -1
6471 Ybomb_e, FALSE, FALSE,
6472 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6475 Ybomb_eB, FALSE, TRUE,
6476 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6479 Ybomb_w, FALSE, FALSE,
6480 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6483 Ybomb_wB, FALSE, TRUE,
6484 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6487 Ybomb_eat, FALSE, FALSE,
6488 EL_BOMB, ACTION_ACTIVATING, -1
6491 Xballoon, TRUE, FALSE,
6495 Yballoon_n, FALSE, FALSE,
6496 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6499 Yballoon_nB, FALSE, TRUE,
6500 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6503 Yballoon_e, FALSE, FALSE,
6504 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6507 Yballoon_eB, FALSE, TRUE,
6508 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6511 Yballoon_s, FALSE, FALSE,
6512 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6515 Yballoon_sB, FALSE, TRUE,
6516 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6519 Yballoon_w, FALSE, FALSE,
6520 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6523 Yballoon_wB, FALSE, TRUE,
6524 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6527 Xgrass, TRUE, FALSE,
6528 EL_EMC_GRASS, -1, -1
6531 Ygrass_nB, FALSE, FALSE,
6532 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6535 Ygrass_eB, FALSE, FALSE,
6536 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6539 Ygrass_sB, FALSE, FALSE,
6540 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6543 Ygrass_wB, FALSE, FALSE,
6544 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6551 Ydirt_nB, FALSE, FALSE,
6552 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6555 Ydirt_eB, FALSE, FALSE,
6556 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6559 Ydirt_sB, FALSE, FALSE,
6560 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6563 Ydirt_wB, FALSE, FALSE,
6564 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6567 Xacid_ne, TRUE, FALSE,
6568 EL_ACID_POOL_TOPRIGHT, -1, -1
6571 Xacid_se, TRUE, FALSE,
6572 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6575 Xacid_s, TRUE, FALSE,
6576 EL_ACID_POOL_BOTTOM, -1, -1
6579 Xacid_sw, TRUE, FALSE,
6580 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6583 Xacid_nw, TRUE, FALSE,
6584 EL_ACID_POOL_TOPLEFT, -1, -1
6587 Xacid_1, TRUE, FALSE,
6591 Xacid_2, FALSE, FALSE,
6595 Xacid_3, FALSE, FALSE,
6599 Xacid_4, FALSE, FALSE,
6603 Xacid_5, FALSE, FALSE,
6607 Xacid_6, FALSE, FALSE,
6611 Xacid_7, FALSE, FALSE,
6615 Xacid_8, FALSE, FALSE,
6619 Xball_1, TRUE, FALSE,
6620 EL_EMC_MAGIC_BALL, -1, -1
6623 Xball_1B, FALSE, FALSE,
6624 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6627 Xball_2, FALSE, FALSE,
6628 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6631 Xball_2B, FALSE, FALSE,
6632 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6635 Yball_eat, FALSE, FALSE,
6636 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6639 Ykey_1_eat, FALSE, FALSE,
6640 EL_EM_KEY_1, ACTION_COLLECTING, -1
6643 Ykey_2_eat, FALSE, FALSE,
6644 EL_EM_KEY_2, ACTION_COLLECTING, -1
6647 Ykey_3_eat, FALSE, FALSE,
6648 EL_EM_KEY_3, ACTION_COLLECTING, -1
6651 Ykey_4_eat, FALSE, FALSE,
6652 EL_EM_KEY_4, ACTION_COLLECTING, -1
6655 Ykey_5_eat, FALSE, FALSE,
6656 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6659 Ykey_6_eat, FALSE, FALSE,
6660 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6663 Ykey_7_eat, FALSE, FALSE,
6664 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6667 Ykey_8_eat, FALSE, FALSE,
6668 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6671 Ylenses_eat, FALSE, FALSE,
6672 EL_EMC_LENSES, ACTION_COLLECTING, -1
6675 Ymagnify_eat, FALSE, FALSE,
6676 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6679 Ygrass_eat, FALSE, FALSE,
6680 EL_EMC_GRASS, ACTION_SNAPPING, -1
6683 Ydirt_eat, FALSE, FALSE,
6684 EL_SAND, ACTION_SNAPPING, -1
6687 Xgrow_ns, TRUE, FALSE,
6688 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6691 Ygrow_ns_eat, FALSE, FALSE,
6692 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6695 Xgrow_ew, TRUE, FALSE,
6696 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6699 Ygrow_ew_eat, FALSE, FALSE,
6700 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6703 Xwonderwall, TRUE, FALSE,
6704 EL_MAGIC_WALL, -1, -1
6707 XwonderwallB, FALSE, FALSE,
6708 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6711 Xamoeba_1, TRUE, FALSE,
6712 EL_AMOEBA_DRY, ACTION_OTHER, -1
6715 Xamoeba_2, FALSE, FALSE,
6716 EL_AMOEBA_DRY, ACTION_OTHER, -1
6719 Xamoeba_3, FALSE, FALSE,
6720 EL_AMOEBA_DRY, ACTION_OTHER, -1
6723 Xamoeba_4, FALSE, FALSE,
6724 EL_AMOEBA_DRY, ACTION_OTHER, -1
6727 Xamoeba_5, TRUE, FALSE,
6728 EL_AMOEBA_WET, ACTION_OTHER, -1
6731 Xamoeba_6, FALSE, FALSE,
6732 EL_AMOEBA_WET, ACTION_OTHER, -1
6735 Xamoeba_7, FALSE, FALSE,
6736 EL_AMOEBA_WET, ACTION_OTHER, -1
6739 Xamoeba_8, FALSE, FALSE,
6740 EL_AMOEBA_WET, ACTION_OTHER, -1
6743 Xdoor_1, TRUE, FALSE,
6744 EL_EM_GATE_1, -1, -1
6747 Xdoor_2, TRUE, FALSE,
6748 EL_EM_GATE_2, -1, -1
6751 Xdoor_3, TRUE, FALSE,
6752 EL_EM_GATE_3, -1, -1
6755 Xdoor_4, TRUE, FALSE,
6756 EL_EM_GATE_4, -1, -1
6759 Xdoor_5, TRUE, FALSE,
6760 EL_EMC_GATE_5, -1, -1
6763 Xdoor_6, TRUE, FALSE,
6764 EL_EMC_GATE_6, -1, -1
6767 Xdoor_7, TRUE, FALSE,
6768 EL_EMC_GATE_7, -1, -1
6771 Xdoor_8, TRUE, FALSE,
6772 EL_EMC_GATE_8, -1, -1
6775 Xkey_1, TRUE, FALSE,
6779 Xkey_2, TRUE, FALSE,
6783 Xkey_3, TRUE, FALSE,
6787 Xkey_4, TRUE, FALSE,
6791 Xkey_5, TRUE, FALSE,
6792 EL_EMC_KEY_5, -1, -1
6795 Xkey_6, TRUE, FALSE,
6796 EL_EMC_KEY_6, -1, -1
6799 Xkey_7, TRUE, FALSE,
6800 EL_EMC_KEY_7, -1, -1
6803 Xkey_8, TRUE, FALSE,
6804 EL_EMC_KEY_8, -1, -1
6807 Xwind_n, TRUE, FALSE,
6808 EL_BALLOON_SWITCH_UP, -1, -1
6811 Xwind_e, TRUE, FALSE,
6812 EL_BALLOON_SWITCH_RIGHT, -1, -1
6815 Xwind_s, TRUE, FALSE,
6816 EL_BALLOON_SWITCH_DOWN, -1, -1
6819 Xwind_w, TRUE, FALSE,
6820 EL_BALLOON_SWITCH_LEFT, -1, -1
6823 Xwind_nesw, TRUE, FALSE,
6824 EL_BALLOON_SWITCH_ANY, -1, -1
6827 Xwind_stop, TRUE, FALSE,
6828 EL_BALLOON_SWITCH_NONE, -1, -1
6832 EL_EM_EXIT_CLOSED, -1, -1
6835 Xexit_1, TRUE, FALSE,
6836 EL_EM_EXIT_OPEN, -1, -1
6839 Xexit_2, FALSE, FALSE,
6840 EL_EM_EXIT_OPEN, -1, -1
6843 Xexit_3, FALSE, FALSE,
6844 EL_EM_EXIT_OPEN, -1, -1
6847 Xdynamite, TRUE, FALSE,
6848 EL_EM_DYNAMITE, -1, -1
6851 Ydynamite_eat, FALSE, FALSE,
6852 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6855 Xdynamite_1, TRUE, FALSE,
6856 EL_EM_DYNAMITE_ACTIVE, -1, -1
6859 Xdynamite_2, FALSE, FALSE,
6860 EL_EM_DYNAMITE_ACTIVE, -1, -1
6863 Xdynamite_3, FALSE, FALSE,
6864 EL_EM_DYNAMITE_ACTIVE, -1, -1
6867 Xdynamite_4, FALSE, FALSE,
6868 EL_EM_DYNAMITE_ACTIVE, -1, -1
6871 Xbumper, TRUE, FALSE,
6872 EL_EMC_SPRING_BUMPER, -1, -1
6875 XbumperB, FALSE, FALSE,
6876 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6879 Xwheel, TRUE, FALSE,
6880 EL_ROBOT_WHEEL, -1, -1
6883 XwheelB, FALSE, FALSE,
6884 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6887 Xswitch, TRUE, FALSE,
6888 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6891 XswitchB, FALSE, FALSE,
6892 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6896 EL_QUICKSAND_EMPTY, -1, -1
6899 Xsand_stone, TRUE, FALSE,
6900 EL_QUICKSAND_FULL, -1, -1
6903 Xsand_stonein_1, FALSE, TRUE,
6904 EL_ROCK, ACTION_FILLING, -1
6907 Xsand_stonein_2, FALSE, TRUE,
6908 EL_ROCK, ACTION_FILLING, -1
6911 Xsand_stonein_3, FALSE, TRUE,
6912 EL_ROCK, ACTION_FILLING, -1
6915 Xsand_stonein_4, FALSE, TRUE,
6916 EL_ROCK, ACTION_FILLING, -1
6919 Xsand_stonesand_1, FALSE, FALSE,
6920 EL_QUICKSAND_EMPTYING, -1, -1
6923 Xsand_stonesand_2, FALSE, FALSE,
6924 EL_QUICKSAND_EMPTYING, -1, -1
6927 Xsand_stonesand_3, FALSE, FALSE,
6928 EL_QUICKSAND_EMPTYING, -1, -1
6931 Xsand_stonesand_4, FALSE, FALSE,
6932 EL_QUICKSAND_EMPTYING, -1, -1
6935 Xsand_stonesand_quickout_1, FALSE, FALSE,
6936 EL_QUICKSAND_EMPTYING, -1, -1
6939 Xsand_stonesand_quickout_2, FALSE, FALSE,
6940 EL_QUICKSAND_EMPTYING, -1, -1
6943 Xsand_stoneout_1, FALSE, FALSE,
6944 EL_ROCK, ACTION_EMPTYING, -1
6947 Xsand_stoneout_2, FALSE, FALSE,
6948 EL_ROCK, ACTION_EMPTYING, -1
6951 Xsand_sandstone_1, FALSE, FALSE,
6952 EL_QUICKSAND_FILLING, -1, -1
6955 Xsand_sandstone_2, FALSE, FALSE,
6956 EL_QUICKSAND_FILLING, -1, -1
6959 Xsand_sandstone_3, FALSE, FALSE,
6960 EL_QUICKSAND_FILLING, -1, -1
6963 Xsand_sandstone_4, FALSE, FALSE,
6964 EL_QUICKSAND_FILLING, -1, -1
6967 Xplant, TRUE, FALSE,
6968 EL_EMC_PLANT, -1, -1
6971 Yplant, FALSE, FALSE,
6972 EL_EMC_PLANT, -1, -1
6975 Xlenses, TRUE, FALSE,
6976 EL_EMC_LENSES, -1, -1
6979 Xmagnify, TRUE, FALSE,
6980 EL_EMC_MAGNIFIER, -1, -1
6983 Xdripper, TRUE, FALSE,
6984 EL_EMC_DRIPPER, -1, -1
6987 XdripperB, FALSE, FALSE,
6988 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6991 Xfake_blank, TRUE, FALSE,
6992 EL_INVISIBLE_WALL, -1, -1
6995 Xfake_blankB, FALSE, FALSE,
6996 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6999 Xfake_grass, TRUE, FALSE,
7000 EL_EMC_FAKE_GRASS, -1, -1
7003 Xfake_grassB, FALSE, FALSE,
7004 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7007 Xfake_door_1, TRUE, FALSE,
7008 EL_EM_GATE_1_GRAY, -1, -1
7011 Xfake_door_2, TRUE, FALSE,
7012 EL_EM_GATE_2_GRAY, -1, -1
7015 Xfake_door_3, TRUE, FALSE,
7016 EL_EM_GATE_3_GRAY, -1, -1
7019 Xfake_door_4, TRUE, FALSE,
7020 EL_EM_GATE_4_GRAY, -1, -1
7023 Xfake_door_5, TRUE, FALSE,
7024 EL_EMC_GATE_5_GRAY, -1, -1
7027 Xfake_door_6, TRUE, FALSE,
7028 EL_EMC_GATE_6_GRAY, -1, -1
7031 Xfake_door_7, TRUE, FALSE,
7032 EL_EMC_GATE_7_GRAY, -1, -1
7035 Xfake_door_8, TRUE, FALSE,
7036 EL_EMC_GATE_8_GRAY, -1, -1
7039 Xfake_acid_1, TRUE, FALSE,
7040 EL_EMC_FAKE_ACID, -1, -1
7043 Xfake_acid_2, FALSE, FALSE,
7044 EL_EMC_FAKE_ACID, -1, -1
7047 Xfake_acid_3, FALSE, FALSE,
7048 EL_EMC_FAKE_ACID, -1, -1
7051 Xfake_acid_4, FALSE, FALSE,
7052 EL_EMC_FAKE_ACID, -1, -1
7055 Xfake_acid_5, FALSE, FALSE,
7056 EL_EMC_FAKE_ACID, -1, -1
7059 Xfake_acid_6, FALSE, FALSE,
7060 EL_EMC_FAKE_ACID, -1, -1
7063 Xfake_acid_7, FALSE, FALSE,
7064 EL_EMC_FAKE_ACID, -1, -1
7067 Xfake_acid_8, FALSE, FALSE,
7068 EL_EMC_FAKE_ACID, -1, -1
7071 Xsteel_1, TRUE, FALSE,
7072 EL_STEELWALL, -1, -1
7075 Xsteel_2, TRUE, FALSE,
7076 EL_EMC_STEELWALL_2, -1, -1
7079 Xsteel_3, TRUE, FALSE,
7080 EL_EMC_STEELWALL_3, -1, -1
7083 Xsteel_4, TRUE, FALSE,
7084 EL_EMC_STEELWALL_4, -1, -1
7087 Xwall_1, TRUE, FALSE,
7091 Xwall_2, TRUE, FALSE,
7092 EL_EMC_WALL_14, -1, -1
7095 Xwall_3, TRUE, FALSE,
7096 EL_EMC_WALL_15, -1, -1
7099 Xwall_4, TRUE, FALSE,
7100 EL_EMC_WALL_16, -1, -1
7103 Xround_wall_1, TRUE, FALSE,
7104 EL_WALL_SLIPPERY, -1, -1
7107 Xround_wall_2, TRUE, FALSE,
7108 EL_EMC_WALL_SLIPPERY_2, -1, -1
7111 Xround_wall_3, TRUE, FALSE,
7112 EL_EMC_WALL_SLIPPERY_3, -1, -1
7115 Xround_wall_4, TRUE, FALSE,
7116 EL_EMC_WALL_SLIPPERY_4, -1, -1
7119 Xdecor_1, TRUE, FALSE,
7120 EL_EMC_WALL_8, -1, -1
7123 Xdecor_2, TRUE, FALSE,
7124 EL_EMC_WALL_6, -1, -1
7127 Xdecor_3, TRUE, FALSE,
7128 EL_EMC_WALL_4, -1, -1
7131 Xdecor_4, TRUE, FALSE,
7132 EL_EMC_WALL_7, -1, -1
7135 Xdecor_5, TRUE, FALSE,
7136 EL_EMC_WALL_5, -1, -1
7139 Xdecor_6, TRUE, FALSE,
7140 EL_EMC_WALL_9, -1, -1
7143 Xdecor_7, TRUE, FALSE,
7144 EL_EMC_WALL_10, -1, -1
7147 Xdecor_8, TRUE, FALSE,
7148 EL_EMC_WALL_1, -1, -1
7151 Xdecor_9, TRUE, FALSE,
7152 EL_EMC_WALL_2, -1, -1
7155 Xdecor_10, TRUE, FALSE,
7156 EL_EMC_WALL_3, -1, -1
7159 Xdecor_11, TRUE, FALSE,
7160 EL_EMC_WALL_11, -1, -1
7163 Xdecor_12, TRUE, FALSE,
7164 EL_EMC_WALL_12, -1, -1
7167 Xalpha_0, TRUE, FALSE,
7168 EL_CHAR('0'), -1, -1
7171 Xalpha_1, TRUE, FALSE,
7172 EL_CHAR('1'), -1, -1
7175 Xalpha_2, TRUE, FALSE,
7176 EL_CHAR('2'), -1, -1
7179 Xalpha_3, TRUE, FALSE,
7180 EL_CHAR('3'), -1, -1
7183 Xalpha_4, TRUE, FALSE,
7184 EL_CHAR('4'), -1, -1
7187 Xalpha_5, TRUE, FALSE,
7188 EL_CHAR('5'), -1, -1
7191 Xalpha_6, TRUE, FALSE,
7192 EL_CHAR('6'), -1, -1
7195 Xalpha_7, TRUE, FALSE,
7196 EL_CHAR('7'), -1, -1
7199 Xalpha_8, TRUE, FALSE,
7200 EL_CHAR('8'), -1, -1
7203 Xalpha_9, TRUE, FALSE,
7204 EL_CHAR('9'), -1, -1
7207 Xalpha_excla, TRUE, FALSE,
7208 EL_CHAR('!'), -1, -1
7211 Xalpha_quote, TRUE, FALSE,
7212 EL_CHAR('"'), -1, -1
7215 Xalpha_comma, TRUE, FALSE,
7216 EL_CHAR(','), -1, -1
7219 Xalpha_minus, TRUE, FALSE,
7220 EL_CHAR('-'), -1, -1
7223 Xalpha_perio, TRUE, FALSE,
7224 EL_CHAR('.'), -1, -1
7227 Xalpha_colon, TRUE, FALSE,
7228 EL_CHAR(':'), -1, -1
7231 Xalpha_quest, TRUE, FALSE,
7232 EL_CHAR('?'), -1, -1
7235 Xalpha_a, TRUE, FALSE,
7236 EL_CHAR('A'), -1, -1
7239 Xalpha_b, TRUE, FALSE,
7240 EL_CHAR('B'), -1, -1
7243 Xalpha_c, TRUE, FALSE,
7244 EL_CHAR('C'), -1, -1
7247 Xalpha_d, TRUE, FALSE,
7248 EL_CHAR('D'), -1, -1
7251 Xalpha_e, TRUE, FALSE,
7252 EL_CHAR('E'), -1, -1
7255 Xalpha_f, TRUE, FALSE,
7256 EL_CHAR('F'), -1, -1
7259 Xalpha_g, TRUE, FALSE,
7260 EL_CHAR('G'), -1, -1
7263 Xalpha_h, TRUE, FALSE,
7264 EL_CHAR('H'), -1, -1
7267 Xalpha_i, TRUE, FALSE,
7268 EL_CHAR('I'), -1, -1
7271 Xalpha_j, TRUE, FALSE,
7272 EL_CHAR('J'), -1, -1
7275 Xalpha_k, TRUE, FALSE,
7276 EL_CHAR('K'), -1, -1
7279 Xalpha_l, TRUE, FALSE,
7280 EL_CHAR('L'), -1, -1
7283 Xalpha_m, TRUE, FALSE,
7284 EL_CHAR('M'), -1, -1
7287 Xalpha_n, TRUE, FALSE,
7288 EL_CHAR('N'), -1, -1
7291 Xalpha_o, TRUE, FALSE,
7292 EL_CHAR('O'), -1, -1
7295 Xalpha_p, TRUE, FALSE,
7296 EL_CHAR('P'), -1, -1
7299 Xalpha_q, TRUE, FALSE,
7300 EL_CHAR('Q'), -1, -1
7303 Xalpha_r, TRUE, FALSE,
7304 EL_CHAR('R'), -1, -1
7307 Xalpha_s, TRUE, FALSE,
7308 EL_CHAR('S'), -1, -1
7311 Xalpha_t, TRUE, FALSE,
7312 EL_CHAR('T'), -1, -1
7315 Xalpha_u, TRUE, FALSE,
7316 EL_CHAR('U'), -1, -1
7319 Xalpha_v, TRUE, FALSE,
7320 EL_CHAR('V'), -1, -1
7323 Xalpha_w, TRUE, FALSE,
7324 EL_CHAR('W'), -1, -1
7327 Xalpha_x, TRUE, FALSE,
7328 EL_CHAR('X'), -1, -1
7331 Xalpha_y, TRUE, FALSE,
7332 EL_CHAR('Y'), -1, -1
7335 Xalpha_z, TRUE, FALSE,
7336 EL_CHAR('Z'), -1, -1
7339 Xalpha_arrow_e, TRUE, FALSE,
7340 EL_CHAR('>'), -1, -1
7343 Xalpha_arrow_w, TRUE, FALSE,
7344 EL_CHAR('<'), -1, -1
7347 Xalpha_copyr, TRUE, FALSE,
7348 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7352 Xboom_bug, FALSE, FALSE,
7353 EL_BUG, ACTION_EXPLODING, -1
7356 Xboom_bomb, FALSE, FALSE,
7357 EL_BOMB, ACTION_EXPLODING, -1
7360 Xboom_android, FALSE, FALSE,
7361 EL_EMC_ANDROID, ACTION_OTHER, -1
7364 Xboom_1, FALSE, FALSE,
7365 EL_DEFAULT, ACTION_EXPLODING, -1
7368 Xboom_2, FALSE, FALSE,
7369 EL_DEFAULT, ACTION_EXPLODING, -1
7372 Znormal, FALSE, FALSE,
7376 Zdynamite, FALSE, FALSE,
7380 Zplayer, FALSE, FALSE,
7384 ZBORDER, FALSE, FALSE,
7394 static struct Mapping_EM_to_RND_player
7403 em_player_mapping_list[] =
7407 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7411 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7415 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7419 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7423 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7427 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7431 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7435 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7439 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7443 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7447 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7451 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7455 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7459 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7463 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7467 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7471 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7475 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7479 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7483 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7487 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7491 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7495 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7499 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7503 EL_PLAYER_1, ACTION_DEFAULT, -1,
7507 EL_PLAYER_2, ACTION_DEFAULT, -1,
7511 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7515 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7519 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7523 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7527 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7531 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7535 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7539 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7543 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7547 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7551 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7555 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7559 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7563 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7567 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7571 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7575 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7579 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7583 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7587 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7591 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7595 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7599 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7603 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7607 EL_PLAYER_3, ACTION_DEFAULT, -1,
7611 EL_PLAYER_4, ACTION_DEFAULT, -1,
7620 int map_element_RND_to_EM(int element_rnd)
7622 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7623 static boolean mapping_initialized = FALSE;
7625 if (!mapping_initialized)
7629 // return "Xalpha_quest" for all undefined elements in mapping array
7630 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7631 mapping_RND_to_EM[i] = Xalpha_quest;
7633 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7634 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7635 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7636 em_object_mapping_list[i].element_em;
7638 mapping_initialized = TRUE;
7641 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7642 return mapping_RND_to_EM[element_rnd];
7644 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7649 int map_element_EM_to_RND(int element_em)
7651 static unsigned short mapping_EM_to_RND[TILE_MAX];
7652 static boolean mapping_initialized = FALSE;
7654 if (!mapping_initialized)
7658 // return "EL_UNKNOWN" for all undefined elements in mapping array
7659 for (i = 0; i < TILE_MAX; i++)
7660 mapping_EM_to_RND[i] = EL_UNKNOWN;
7662 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7663 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7664 em_object_mapping_list[i].element_rnd;
7666 mapping_initialized = TRUE;
7669 if (element_em >= 0 && element_em < TILE_MAX)
7670 return mapping_EM_to_RND[element_em];
7672 Error(ERR_WARN, "invalid EM level element %d", element_em);
7677 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7679 struct LevelInfo_EM *level_em = level->native_em_level;
7680 struct LEVEL *lev = level_em->lev;
7683 for (i = 0; i < TILE_MAX; i++)
7684 lev->android_array[i] = Xblank;
7686 for (i = 0; i < level->num_android_clone_elements; i++)
7688 int element_rnd = level->android_clone_element[i];
7689 int element_em = map_element_RND_to_EM(element_rnd);
7691 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7692 if (em_object_mapping_list[j].element_rnd == element_rnd)
7693 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7697 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7699 struct LevelInfo_EM *level_em = level->native_em_level;
7700 struct LEVEL *lev = level_em->lev;
7703 level->num_android_clone_elements = 0;
7705 for (i = 0; i < TILE_MAX; i++)
7707 int element_em = lev->android_array[i];
7709 boolean element_found = FALSE;
7711 if (element_em == Xblank)
7714 element_rnd = map_element_EM_to_RND(element_em);
7716 for (j = 0; j < level->num_android_clone_elements; j++)
7717 if (level->android_clone_element[j] == element_rnd)
7718 element_found = TRUE;
7722 level->android_clone_element[level->num_android_clone_elements++] =
7725 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7730 if (level->num_android_clone_elements == 0)
7732 level->num_android_clone_elements = 1;
7733 level->android_clone_element[0] = EL_EMPTY;
7737 int map_direction_RND_to_EM(int direction)
7739 return (direction == MV_UP ? 0 :
7740 direction == MV_RIGHT ? 1 :
7741 direction == MV_DOWN ? 2 :
7742 direction == MV_LEFT ? 3 :
7746 int map_direction_EM_to_RND(int direction)
7748 return (direction == 0 ? MV_UP :
7749 direction == 1 ? MV_RIGHT :
7750 direction == 2 ? MV_DOWN :
7751 direction == 3 ? MV_LEFT :
7755 int map_element_RND_to_SP(int element_rnd)
7757 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7759 if (element_rnd >= EL_SP_START &&
7760 element_rnd <= EL_SP_END)
7761 element_sp = element_rnd - EL_SP_START;
7762 else if (element_rnd == EL_EMPTY_SPACE)
7764 else if (element_rnd == EL_INVISIBLE_WALL)
7770 int map_element_SP_to_RND(int element_sp)
7772 int element_rnd = EL_UNKNOWN;
7774 if (element_sp >= 0x00 &&
7776 element_rnd = EL_SP_START + element_sp;
7777 else if (element_sp == 0x28)
7778 element_rnd = EL_INVISIBLE_WALL;
7783 int map_action_SP_to_RND(int action_sp)
7787 case actActive: return ACTION_ACTIVE;
7788 case actImpact: return ACTION_IMPACT;
7789 case actExploding: return ACTION_EXPLODING;
7790 case actDigging: return ACTION_DIGGING;
7791 case actSnapping: return ACTION_SNAPPING;
7792 case actCollecting: return ACTION_COLLECTING;
7793 case actPassing: return ACTION_PASSING;
7794 case actPushing: return ACTION_PUSHING;
7795 case actDropping: return ACTION_DROPPING;
7797 default: return ACTION_DEFAULT;
7801 int map_element_RND_to_MM(int element_rnd)
7803 return (element_rnd >= EL_MM_START_1 &&
7804 element_rnd <= EL_MM_END_1 ?
7805 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7807 element_rnd >= EL_MM_START_2 &&
7808 element_rnd <= EL_MM_END_2 ?
7809 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7811 element_rnd >= EL_CHAR_START &&
7812 element_rnd <= EL_CHAR_END ?
7813 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7815 element_rnd >= EL_MM_RUNTIME_START &&
7816 element_rnd <= EL_MM_RUNTIME_END ?
7817 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7819 element_rnd >= EL_MM_DUMMY_START &&
7820 element_rnd <= EL_MM_DUMMY_END ?
7821 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7823 EL_MM_EMPTY_NATIVE);
7826 int map_element_MM_to_RND(int element_mm)
7828 return (element_mm == EL_MM_EMPTY_NATIVE ||
7829 element_mm == EL_DF_EMPTY_NATIVE ?
7832 element_mm >= EL_MM_START_1_NATIVE &&
7833 element_mm <= EL_MM_END_1_NATIVE ?
7834 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7836 element_mm >= EL_MM_START_2_NATIVE &&
7837 element_mm <= EL_MM_END_2_NATIVE ?
7838 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7840 element_mm >= EL_MM_CHAR_START_NATIVE &&
7841 element_mm <= EL_MM_CHAR_END_NATIVE ?
7842 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7844 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7845 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7846 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7848 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7849 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7850 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7855 int map_action_MM_to_RND(int action_mm)
7857 // all MM actions are defined to exactly match their RND counterparts
7861 int map_sound_MM_to_RND(int sound_mm)
7865 case SND_MM_GAME_LEVELTIME_CHARGING:
7866 return SND_GAME_LEVELTIME_CHARGING;
7868 case SND_MM_GAME_HEALTH_CHARGING:
7869 return SND_GAME_HEALTH_CHARGING;
7872 return SND_UNDEFINED;
7876 int map_mm_wall_element(int element)
7878 return (element >= EL_MM_STEEL_WALL_START &&
7879 element <= EL_MM_STEEL_WALL_END ?
7882 element >= EL_MM_WOODEN_WALL_START &&
7883 element <= EL_MM_WOODEN_WALL_END ?
7886 element >= EL_MM_ICE_WALL_START &&
7887 element <= EL_MM_ICE_WALL_END ?
7890 element >= EL_MM_AMOEBA_WALL_START &&
7891 element <= EL_MM_AMOEBA_WALL_END ?
7894 element >= EL_DF_STEEL_WALL_START &&
7895 element <= EL_DF_STEEL_WALL_END ?
7898 element >= EL_DF_WOODEN_WALL_START &&
7899 element <= EL_DF_WOODEN_WALL_END ?
7905 int map_mm_wall_element_editor(int element)
7909 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7910 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7911 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7912 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7913 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7914 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7916 default: return element;
7920 int get_next_element(int element)
7924 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7925 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7926 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7927 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7928 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7929 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7930 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7931 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7932 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7933 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7934 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7936 default: return element;
7940 int el2img_mm(int element_mm)
7942 return el2img(map_element_MM_to_RND(element_mm));
7945 int el_act_dir2img(int element, int action, int direction)
7947 element = GFX_ELEMENT(element);
7948 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7950 // direction_graphic[][] == graphic[] for undefined direction graphics
7951 return element_info[element].direction_graphic[action][direction];
7954 static int el_act_dir2crm(int element, int action, int direction)
7956 element = GFX_ELEMENT(element);
7957 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7959 // direction_graphic[][] == graphic[] for undefined direction graphics
7960 return element_info[element].direction_crumbled[action][direction];
7963 int el_act2img(int element, int action)
7965 element = GFX_ELEMENT(element);
7967 return element_info[element].graphic[action];
7970 int el_act2crm(int element, int action)
7972 element = GFX_ELEMENT(element);
7974 return element_info[element].crumbled[action];
7977 int el_dir2img(int element, int direction)
7979 element = GFX_ELEMENT(element);
7981 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7984 int el2baseimg(int element)
7986 return element_info[element].graphic[ACTION_DEFAULT];
7989 int el2img(int element)
7991 element = GFX_ELEMENT(element);
7993 return element_info[element].graphic[ACTION_DEFAULT];
7996 int el2edimg(int element)
7998 element = GFX_ELEMENT(element);
8000 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8003 int el2preimg(int element)
8005 element = GFX_ELEMENT(element);
8007 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8010 int el2panelimg(int element)
8012 element = GFX_ELEMENT(element);
8014 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8017 int font2baseimg(int font_nr)
8019 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8022 int getBeltNrFromBeltElement(int element)
8024 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8025 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8026 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8029 int getBeltNrFromBeltActiveElement(int element)
8031 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8032 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8033 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8036 int getBeltNrFromBeltSwitchElement(int element)
8038 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8039 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8040 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8043 int getBeltDirNrFromBeltElement(int element)
8045 static int belt_base_element[4] =
8047 EL_CONVEYOR_BELT_1_LEFT,
8048 EL_CONVEYOR_BELT_2_LEFT,
8049 EL_CONVEYOR_BELT_3_LEFT,
8050 EL_CONVEYOR_BELT_4_LEFT
8053 int belt_nr = getBeltNrFromBeltElement(element);
8054 int belt_dir_nr = element - belt_base_element[belt_nr];
8056 return (belt_dir_nr % 3);
8059 int getBeltDirNrFromBeltSwitchElement(int element)
8061 static int belt_base_element[4] =
8063 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8064 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8065 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8066 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8069 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8070 int belt_dir_nr = element - belt_base_element[belt_nr];
8072 return (belt_dir_nr % 3);
8075 int getBeltDirFromBeltElement(int element)
8077 static int belt_move_dir[3] =
8084 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8086 return belt_move_dir[belt_dir_nr];
8089 int getBeltDirFromBeltSwitchElement(int element)
8091 static int belt_move_dir[3] =
8098 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8100 return belt_move_dir[belt_dir_nr];
8103 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8105 static int belt_base_element[4] =
8107 EL_CONVEYOR_BELT_1_LEFT,
8108 EL_CONVEYOR_BELT_2_LEFT,
8109 EL_CONVEYOR_BELT_3_LEFT,
8110 EL_CONVEYOR_BELT_4_LEFT
8113 return belt_base_element[belt_nr] + belt_dir_nr;
8116 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8118 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8120 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8123 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8125 static int belt_base_element[4] =
8127 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8128 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8129 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8130 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8133 return belt_base_element[belt_nr] + belt_dir_nr;
8136 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8138 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8140 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8143 boolean getTeamMode_EM(void)
8145 return game.team_mode || network_playing;
8148 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8150 int game_frame_delay_value;
8152 game_frame_delay_value =
8153 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8154 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8157 if (tape.playing && tape.warp_forward && !tape.pausing)
8158 game_frame_delay_value = 0;
8160 return game_frame_delay_value;
8163 unsigned int InitRND(int seed)
8165 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8166 return InitEngineRandom_EM(seed);
8167 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8168 return InitEngineRandom_SP(seed);
8169 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8170 return InitEngineRandom_MM(seed);
8172 return InitEngineRandom_RND(seed);
8175 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8176 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8178 static int get_effective_element_EM(int tile, int frame_em)
8180 int element = object_mapping[tile].element_rnd;
8181 int action = object_mapping[tile].action;
8182 boolean is_backside = object_mapping[tile].is_backside;
8183 boolean action_removing = (action == ACTION_DIGGING ||
8184 action == ACTION_SNAPPING ||
8185 action == ACTION_COLLECTING);
8191 case Yacid_splash_eB:
8192 case Yacid_splash_wB:
8193 return (frame_em > 5 ? EL_EMPTY : element);
8199 else // frame_em == 7
8203 case Yacid_splash_eB:
8204 case Yacid_splash_wB:
8207 case Yemerald_stone:
8210 case Ydiamond_stone:
8214 case Xdrip_stretchB:
8233 case Xsand_stonein_1:
8234 case Xsand_stonein_2:
8235 case Xsand_stonein_3:
8236 case Xsand_stonein_4:
8240 return (is_backside || action_removing ? EL_EMPTY : element);
8245 static boolean check_linear_animation_EM(int tile)
8249 case Xsand_stonesand_1:
8250 case Xsand_stonesand_quickout_1:
8251 case Xsand_sandstone_1:
8252 case Xsand_stonein_1:
8253 case Xsand_stoneout_1:
8272 case Yacid_splash_eB:
8273 case Yacid_splash_wB:
8274 case Yemerald_stone:
8281 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8282 boolean has_crumbled_graphics,
8283 int crumbled, int sync_frame)
8285 // if element can be crumbled, but certain action graphics are just empty
8286 // space (like instantly snapping sand to empty space in 1 frame), do not
8287 // treat these empty space graphics as crumbled graphics in EMC engine
8288 if (crumbled == IMG_EMPTY_SPACE)
8289 has_crumbled_graphics = FALSE;
8291 if (has_crumbled_graphics)
8293 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8294 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8295 g_crumbled->anim_delay,
8296 g_crumbled->anim_mode,
8297 g_crumbled->anim_start_frame,
8300 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8301 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8303 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8304 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8306 g_em->has_crumbled_graphics = TRUE;
8310 g_em->crumbled_bitmap = NULL;
8311 g_em->crumbled_src_x = 0;
8312 g_em->crumbled_src_y = 0;
8313 g_em->crumbled_border_size = 0;
8314 g_em->crumbled_tile_size = 0;
8316 g_em->has_crumbled_graphics = FALSE;
8321 void ResetGfxAnimation_EM(int x, int y, int tile)
8327 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8328 int tile, int frame_em, int x, int y)
8330 int action = object_mapping[tile].action;
8331 int direction = object_mapping[tile].direction;
8332 int effective_element = get_effective_element_EM(tile, frame_em);
8333 int graphic = (direction == MV_NONE ?
8334 el_act2img(effective_element, action) :
8335 el_act_dir2img(effective_element, action, direction));
8336 struct GraphicInfo *g = &graphic_info[graphic];
8338 boolean action_removing = (action == ACTION_DIGGING ||
8339 action == ACTION_SNAPPING ||
8340 action == ACTION_COLLECTING);
8341 boolean action_moving = (action == ACTION_FALLING ||
8342 action == ACTION_MOVING ||
8343 action == ACTION_PUSHING ||
8344 action == ACTION_EATING ||
8345 action == ACTION_FILLING ||
8346 action == ACTION_EMPTYING);
8347 boolean action_falling = (action == ACTION_FALLING ||
8348 action == ACTION_FILLING ||
8349 action == ACTION_EMPTYING);
8351 // special case: graphic uses "2nd movement tile" and has defined
8352 // 7 frames for movement animation (or less) => use default graphic
8353 // for last (8th) frame which ends the movement animation
8354 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8356 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8357 graphic = (direction == MV_NONE ?
8358 el_act2img(effective_element, action) :
8359 el_act_dir2img(effective_element, action, direction));
8361 g = &graphic_info[graphic];
8364 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8368 else if (action_moving)
8370 boolean is_backside = object_mapping[tile].is_backside;
8374 int direction = object_mapping[tile].direction;
8375 int move_dir = (action_falling ? MV_DOWN : direction);
8380 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8381 if (g->double_movement && frame_em == 0)
8385 if (move_dir == MV_LEFT)
8386 GfxFrame[x - 1][y] = GfxFrame[x][y];
8387 else if (move_dir == MV_RIGHT)
8388 GfxFrame[x + 1][y] = GfxFrame[x][y];
8389 else if (move_dir == MV_UP)
8390 GfxFrame[x][y - 1] = GfxFrame[x][y];
8391 else if (move_dir == MV_DOWN)
8392 GfxFrame[x][y + 1] = GfxFrame[x][y];
8399 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8400 if (tile == Xsand_stonesand_quickout_1 ||
8401 tile == Xsand_stonesand_quickout_2)
8405 if (graphic_info[graphic].anim_global_sync)
8406 sync_frame = FrameCounter;
8407 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8408 sync_frame = GfxFrame[x][y];
8410 sync_frame = 0; // playfield border (pseudo steel)
8412 SetRandomAnimationValue(x, y);
8414 int frame = getAnimationFrame(g->anim_frames,
8417 g->anim_start_frame,
8420 g_em->unique_identifier =
8421 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8424 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8425 int tile, int frame_em, int x, int y)
8427 int action = object_mapping[tile].action;
8428 int direction = object_mapping[tile].direction;
8429 boolean is_backside = object_mapping[tile].is_backside;
8430 int effective_element = get_effective_element_EM(tile, frame_em);
8431 int effective_action = action;
8432 int graphic = (direction == MV_NONE ?
8433 el_act2img(effective_element, effective_action) :
8434 el_act_dir2img(effective_element, effective_action,
8436 int crumbled = (direction == MV_NONE ?
8437 el_act2crm(effective_element, effective_action) :
8438 el_act_dir2crm(effective_element, effective_action,
8440 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8441 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8442 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8443 struct GraphicInfo *g = &graphic_info[graphic];
8446 // special case: graphic uses "2nd movement tile" and has defined
8447 // 7 frames for movement animation (or less) => use default graphic
8448 // for last (8th) frame which ends the movement animation
8449 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8451 effective_action = ACTION_DEFAULT;
8452 graphic = (direction == MV_NONE ?
8453 el_act2img(effective_element, effective_action) :
8454 el_act_dir2img(effective_element, effective_action,
8456 crumbled = (direction == MV_NONE ?
8457 el_act2crm(effective_element, effective_action) :
8458 el_act_dir2crm(effective_element, effective_action,
8461 g = &graphic_info[graphic];
8464 if (graphic_info[graphic].anim_global_sync)
8465 sync_frame = FrameCounter;
8466 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8467 sync_frame = GfxFrame[x][y];
8469 sync_frame = 0; // playfield border (pseudo steel)
8471 SetRandomAnimationValue(x, y);
8473 int frame = getAnimationFrame(g->anim_frames,
8476 g->anim_start_frame,
8479 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8480 g->double_movement && is_backside);
8482 // (updating the "crumbled" graphic definitions is probably not really needed,
8483 // as animations for crumbled graphics can't be longer than one EMC cycle)
8484 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8488 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8489 int player_nr, int anim, int frame_em)
8491 int element = player_mapping[player_nr][anim].element_rnd;
8492 int action = player_mapping[player_nr][anim].action;
8493 int direction = player_mapping[player_nr][anim].direction;
8494 int graphic = (direction == MV_NONE ?
8495 el_act2img(element, action) :
8496 el_act_dir2img(element, action, direction));
8497 struct GraphicInfo *g = &graphic_info[graphic];
8500 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8502 stored_player[player_nr].StepFrame = frame_em;
8504 sync_frame = stored_player[player_nr].Frame;
8506 int frame = getAnimationFrame(g->anim_frames,
8509 g->anim_start_frame,
8512 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8513 &g_em->src_x, &g_em->src_y, FALSE);
8516 void InitGraphicInfo_EM(void)
8521 int num_em_gfx_errors = 0;
8523 if (graphic_info_em_object[0][0].bitmap == NULL)
8525 // EM graphics not yet initialized in em_open_all()
8530 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8533 // always start with reliable default values
8534 for (i = 0; i < TILE_MAX; i++)
8536 object_mapping[i].element_rnd = EL_UNKNOWN;
8537 object_mapping[i].is_backside = FALSE;
8538 object_mapping[i].action = ACTION_DEFAULT;
8539 object_mapping[i].direction = MV_NONE;
8542 // always start with reliable default values
8543 for (p = 0; p < MAX_PLAYERS; p++)
8545 for (i = 0; i < SPR_MAX; i++)
8547 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8548 player_mapping[p][i].action = ACTION_DEFAULT;
8549 player_mapping[p][i].direction = MV_NONE;
8553 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8555 int e = em_object_mapping_list[i].element_em;
8557 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8558 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8560 if (em_object_mapping_list[i].action != -1)
8561 object_mapping[e].action = em_object_mapping_list[i].action;
8563 if (em_object_mapping_list[i].direction != -1)
8564 object_mapping[e].direction =
8565 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8568 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8570 int a = em_player_mapping_list[i].action_em;
8571 int p = em_player_mapping_list[i].player_nr;
8573 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8575 if (em_player_mapping_list[i].action != -1)
8576 player_mapping[p][a].action = em_player_mapping_list[i].action;
8578 if (em_player_mapping_list[i].direction != -1)
8579 player_mapping[p][a].direction =
8580 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8583 for (i = 0; i < TILE_MAX; i++)
8585 int element = object_mapping[i].element_rnd;
8586 int action = object_mapping[i].action;
8587 int direction = object_mapping[i].direction;
8588 boolean is_backside = object_mapping[i].is_backside;
8589 boolean action_exploding = ((action == ACTION_EXPLODING ||
8590 action == ACTION_SMASHED_BY_ROCK ||
8591 action == ACTION_SMASHED_BY_SPRING) &&
8592 element != EL_DIAMOND);
8593 boolean action_active = (action == ACTION_ACTIVE);
8594 boolean action_other = (action == ACTION_OTHER);
8596 for (j = 0; j < 8; j++)
8598 int effective_element = get_effective_element_EM(i, j);
8599 int effective_action = (j < 7 ? action :
8600 i == Xdrip_stretch ? action :
8601 i == Xdrip_stretchB ? action :
8602 i == Ydrip_s1 ? action :
8603 i == Ydrip_s1B ? action :
8604 i == Xball_1B ? action :
8605 i == Xball_2 ? action :
8606 i == Xball_2B ? action :
8607 i == Yball_eat ? action :
8608 i == Ykey_1_eat ? action :
8609 i == Ykey_2_eat ? action :
8610 i == Ykey_3_eat ? action :
8611 i == Ykey_4_eat ? action :
8612 i == Ykey_5_eat ? action :
8613 i == Ykey_6_eat ? action :
8614 i == Ykey_7_eat ? action :
8615 i == Ykey_8_eat ? action :
8616 i == Ylenses_eat ? action :
8617 i == Ymagnify_eat ? action :
8618 i == Ygrass_eat ? action :
8619 i == Ydirt_eat ? action :
8620 i == Xsand_stonein_1 ? action :
8621 i == Xsand_stonein_2 ? action :
8622 i == Xsand_stonein_3 ? action :
8623 i == Xsand_stonein_4 ? action :
8624 i == Xsand_stoneout_1 ? action :
8625 i == Xsand_stoneout_2 ? action :
8626 i == Xboom_android ? ACTION_EXPLODING :
8627 action_exploding ? ACTION_EXPLODING :
8628 action_active ? action :
8629 action_other ? action :
8631 int graphic = (el_act_dir2img(effective_element, effective_action,
8633 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8635 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8636 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8637 boolean has_action_graphics = (graphic != base_graphic);
8638 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8639 struct GraphicInfo *g = &graphic_info[graphic];
8640 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8643 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8644 boolean special_animation = (action != ACTION_DEFAULT &&
8645 g->anim_frames == 3 &&
8646 g->anim_delay == 2 &&
8647 g->anim_mode & ANIM_LINEAR);
8648 int sync_frame = (i == Xdrip_stretch ? 7 :
8649 i == Xdrip_stretchB ? 7 :
8650 i == Ydrip_s2 ? j + 8 :
8651 i == Ydrip_s2B ? j + 8 :
8660 i == Xfake_acid_1 ? 0 :
8661 i == Xfake_acid_2 ? 10 :
8662 i == Xfake_acid_3 ? 20 :
8663 i == Xfake_acid_4 ? 30 :
8664 i == Xfake_acid_5 ? 40 :
8665 i == Xfake_acid_6 ? 50 :
8666 i == Xfake_acid_7 ? 60 :
8667 i == Xfake_acid_8 ? 70 :
8669 i == Xball_2B ? j + 8 :
8670 i == Yball_eat ? j + 1 :
8671 i == Ykey_1_eat ? j + 1 :
8672 i == Ykey_2_eat ? j + 1 :
8673 i == Ykey_3_eat ? j + 1 :
8674 i == Ykey_4_eat ? j + 1 :
8675 i == Ykey_5_eat ? j + 1 :
8676 i == Ykey_6_eat ? j + 1 :
8677 i == Ykey_7_eat ? j + 1 :
8678 i == Ykey_8_eat ? j + 1 :
8679 i == Ylenses_eat ? j + 1 :
8680 i == Ymagnify_eat ? j + 1 :
8681 i == Ygrass_eat ? j + 1 :
8682 i == Ydirt_eat ? j + 1 :
8683 i == Xamoeba_1 ? 0 :
8684 i == Xamoeba_2 ? 1 :
8685 i == Xamoeba_3 ? 2 :
8686 i == Xamoeba_4 ? 3 :
8687 i == Xamoeba_5 ? 0 :
8688 i == Xamoeba_6 ? 1 :
8689 i == Xamoeba_7 ? 2 :
8690 i == Xamoeba_8 ? 3 :
8691 i == Xexit_2 ? j + 8 :
8692 i == Xexit_3 ? j + 16 :
8693 i == Xdynamite_1 ? 0 :
8694 i == Xdynamite_2 ? 8 :
8695 i == Xdynamite_3 ? 16 :
8696 i == Xdynamite_4 ? 24 :
8697 i == Xsand_stonein_1 ? j + 1 :
8698 i == Xsand_stonein_2 ? j + 9 :
8699 i == Xsand_stonein_3 ? j + 17 :
8700 i == Xsand_stonein_4 ? j + 25 :
8701 i == Xsand_stoneout_1 && j == 0 ? 0 :
8702 i == Xsand_stoneout_1 && j == 1 ? 0 :
8703 i == Xsand_stoneout_1 && j == 2 ? 1 :
8704 i == Xsand_stoneout_1 && j == 3 ? 2 :
8705 i == Xsand_stoneout_1 && j == 4 ? 2 :
8706 i == Xsand_stoneout_1 && j == 5 ? 3 :
8707 i == Xsand_stoneout_1 && j == 6 ? 4 :
8708 i == Xsand_stoneout_1 && j == 7 ? 4 :
8709 i == Xsand_stoneout_2 && j == 0 ? 5 :
8710 i == Xsand_stoneout_2 && j == 1 ? 6 :
8711 i == Xsand_stoneout_2 && j == 2 ? 7 :
8712 i == Xsand_stoneout_2 && j == 3 ? 8 :
8713 i == Xsand_stoneout_2 && j == 4 ? 9 :
8714 i == Xsand_stoneout_2 && j == 5 ? 11 :
8715 i == Xsand_stoneout_2 && j == 6 ? 13 :
8716 i == Xsand_stoneout_2 && j == 7 ? 15 :
8717 i == Xboom_bug && j == 1 ? 2 :
8718 i == Xboom_bug && j == 2 ? 2 :
8719 i == Xboom_bug && j == 3 ? 4 :
8720 i == Xboom_bug && j == 4 ? 4 :
8721 i == Xboom_bug && j == 5 ? 2 :
8722 i == Xboom_bug && j == 6 ? 2 :
8723 i == Xboom_bug && j == 7 ? 0 :
8724 i == Xboom_bomb && j == 1 ? 2 :
8725 i == Xboom_bomb && j == 2 ? 2 :
8726 i == Xboom_bomb && j == 3 ? 4 :
8727 i == Xboom_bomb && j == 4 ? 4 :
8728 i == Xboom_bomb && j == 5 ? 2 :
8729 i == Xboom_bomb && j == 6 ? 2 :
8730 i == Xboom_bomb && j == 7 ? 0 :
8731 i == Xboom_android && j == 7 ? 6 :
8732 i == Xboom_1 && j == 1 ? 2 :
8733 i == Xboom_1 && j == 2 ? 2 :
8734 i == Xboom_1 && j == 3 ? 4 :
8735 i == Xboom_1 && j == 4 ? 4 :
8736 i == Xboom_1 && j == 5 ? 6 :
8737 i == Xboom_1 && j == 6 ? 6 :
8738 i == Xboom_1 && j == 7 ? 8 :
8739 i == Xboom_2 && j == 0 ? 8 :
8740 i == Xboom_2 && j == 1 ? 8 :
8741 i == Xboom_2 && j == 2 ? 10 :
8742 i == Xboom_2 && j == 3 ? 10 :
8743 i == Xboom_2 && j == 4 ? 10 :
8744 i == Xboom_2 && j == 5 ? 12 :
8745 i == Xboom_2 && j == 6 ? 12 :
8746 i == Xboom_2 && j == 7 ? 12 :
8747 special_animation && j == 4 ? 3 :
8748 effective_action != action ? 0 :
8752 Bitmap *debug_bitmap = g_em->bitmap;
8753 int debug_src_x = g_em->src_x;
8754 int debug_src_y = g_em->src_y;
8757 int frame = getAnimationFrame(g->anim_frames,
8760 g->anim_start_frame,
8763 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8764 g->double_movement && is_backside);
8766 g_em->bitmap = src_bitmap;
8767 g_em->src_x = src_x;
8768 g_em->src_y = src_y;
8769 g_em->src_offset_x = 0;
8770 g_em->src_offset_y = 0;
8771 g_em->dst_offset_x = 0;
8772 g_em->dst_offset_y = 0;
8773 g_em->width = TILEX;
8774 g_em->height = TILEY;
8776 g_em->preserve_background = FALSE;
8778 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8781 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8782 effective_action == ACTION_MOVING ||
8783 effective_action == ACTION_PUSHING ||
8784 effective_action == ACTION_EATING)) ||
8785 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8786 effective_action == ACTION_EMPTYING)))
8789 (effective_action == ACTION_FALLING ||
8790 effective_action == ACTION_FILLING ||
8791 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8792 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8793 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8794 int num_steps = (i == Ydrip_s1 ? 16 :
8795 i == Ydrip_s1B ? 16 :
8796 i == Ydrip_s2 ? 16 :
8797 i == Ydrip_s2B ? 16 :
8798 i == Xsand_stonein_1 ? 32 :
8799 i == Xsand_stonein_2 ? 32 :
8800 i == Xsand_stonein_3 ? 32 :
8801 i == Xsand_stonein_4 ? 32 :
8802 i == Xsand_stoneout_1 ? 16 :
8803 i == Xsand_stoneout_2 ? 16 : 8);
8804 int cx = ABS(dx) * (TILEX / num_steps);
8805 int cy = ABS(dy) * (TILEY / num_steps);
8806 int step_frame = (i == Ydrip_s2 ? j + 8 :
8807 i == Ydrip_s2B ? j + 8 :
8808 i == Xsand_stonein_2 ? j + 8 :
8809 i == Xsand_stonein_3 ? j + 16 :
8810 i == Xsand_stonein_4 ? j + 24 :
8811 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8812 int step = (is_backside ? step_frame : num_steps - step_frame);
8814 if (is_backside) // tile where movement starts
8816 if (dx < 0 || dy < 0)
8818 g_em->src_offset_x = cx * step;
8819 g_em->src_offset_y = cy * step;
8823 g_em->dst_offset_x = cx * step;
8824 g_em->dst_offset_y = cy * step;
8827 else // tile where movement ends
8829 if (dx < 0 || dy < 0)
8831 g_em->dst_offset_x = cx * step;
8832 g_em->dst_offset_y = cy * step;
8836 g_em->src_offset_x = cx * step;
8837 g_em->src_offset_y = cy * step;
8841 g_em->width = TILEX - cx * step;
8842 g_em->height = TILEY - cy * step;
8845 // create unique graphic identifier to decide if tile must be redrawn
8846 /* bit 31 - 16 (16 bit): EM style graphic
8847 bit 15 - 12 ( 4 bit): EM style frame
8848 bit 11 - 6 ( 6 bit): graphic width
8849 bit 5 - 0 ( 6 bit): graphic height */
8850 g_em->unique_identifier =
8851 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8855 // skip check for EMC elements not contained in original EMC artwork
8856 if (element == EL_EMC_FAKE_ACID)
8859 if (g_em->bitmap != debug_bitmap ||
8860 g_em->src_x != debug_src_x ||
8861 g_em->src_y != debug_src_y ||
8862 g_em->src_offset_x != 0 ||
8863 g_em->src_offset_y != 0 ||
8864 g_em->dst_offset_x != 0 ||
8865 g_em->dst_offset_y != 0 ||
8866 g_em->width != TILEX ||
8867 g_em->height != TILEY)
8869 static int last_i = -1;
8877 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8878 i, element, element_info[element].token_name,
8879 element_action_info[effective_action].suffix, direction);
8881 if (element != effective_element)
8882 printf(" [%d ('%s')]",
8884 element_info[effective_element].token_name);
8888 if (g_em->bitmap != debug_bitmap)
8889 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8890 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8892 if (g_em->src_x != debug_src_x ||
8893 g_em->src_y != debug_src_y)
8894 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8895 j, (is_backside ? 'B' : 'F'),
8896 g_em->src_x, g_em->src_y,
8897 g_em->src_x / 32, g_em->src_y / 32,
8898 debug_src_x, debug_src_y,
8899 debug_src_x / 32, debug_src_y / 32);
8901 if (g_em->src_offset_x != 0 ||
8902 g_em->src_offset_y != 0 ||
8903 g_em->dst_offset_x != 0 ||
8904 g_em->dst_offset_y != 0)
8905 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8907 g_em->src_offset_x, g_em->src_offset_y,
8908 g_em->dst_offset_x, g_em->dst_offset_y);
8910 if (g_em->width != TILEX ||
8911 g_em->height != TILEY)
8912 printf(" %d (%d): size %d,%d should be %d,%d\n",
8914 g_em->width, g_em->height, TILEX, TILEY);
8916 num_em_gfx_errors++;
8923 for (i = 0; i < TILE_MAX; i++)
8925 for (j = 0; j < 8; j++)
8927 int element = object_mapping[i].element_rnd;
8928 int action = object_mapping[i].action;
8929 int direction = object_mapping[i].direction;
8930 boolean is_backside = object_mapping[i].is_backside;
8931 int graphic_action = el_act_dir2img(element, action, direction);
8932 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8934 if ((action == ACTION_SMASHED_BY_ROCK ||
8935 action == ACTION_SMASHED_BY_SPRING ||
8936 action == ACTION_EATING) &&
8937 graphic_action == graphic_default)
8939 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8940 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8941 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8942 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8945 // no separate animation for "smashed by rock" -- use rock instead
8946 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8947 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8949 g_em->bitmap = g_xx->bitmap;
8950 g_em->src_x = g_xx->src_x;
8951 g_em->src_y = g_xx->src_y;
8952 g_em->src_offset_x = g_xx->src_offset_x;
8953 g_em->src_offset_y = g_xx->src_offset_y;
8954 g_em->dst_offset_x = g_xx->dst_offset_x;
8955 g_em->dst_offset_y = g_xx->dst_offset_y;
8956 g_em->width = g_xx->width;
8957 g_em->height = g_xx->height;
8958 g_em->unique_identifier = g_xx->unique_identifier;
8961 g_em->preserve_background = TRUE;
8966 for (p = 0; p < MAX_PLAYERS; p++)
8968 for (i = 0; i < SPR_MAX; i++)
8970 int element = player_mapping[p][i].element_rnd;
8971 int action = player_mapping[p][i].action;
8972 int direction = player_mapping[p][i].direction;
8974 for (j = 0; j < 8; j++)
8976 int effective_element = element;
8977 int effective_action = action;
8978 int graphic = (direction == MV_NONE ?
8979 el_act2img(effective_element, effective_action) :
8980 el_act_dir2img(effective_element, effective_action,
8982 struct GraphicInfo *g = &graphic_info[graphic];
8983 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8989 Bitmap *debug_bitmap = g_em->bitmap;
8990 int debug_src_x = g_em->src_x;
8991 int debug_src_y = g_em->src_y;
8994 int frame = getAnimationFrame(g->anim_frames,
8997 g->anim_start_frame,
9000 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9002 g_em->bitmap = src_bitmap;
9003 g_em->src_x = src_x;
9004 g_em->src_y = src_y;
9005 g_em->src_offset_x = 0;
9006 g_em->src_offset_y = 0;
9007 g_em->dst_offset_x = 0;
9008 g_em->dst_offset_y = 0;
9009 g_em->width = TILEX;
9010 g_em->height = TILEY;
9014 // skip check for EMC elements not contained in original EMC artwork
9015 if (element == EL_PLAYER_3 ||
9016 element == EL_PLAYER_4)
9019 if (g_em->bitmap != debug_bitmap ||
9020 g_em->src_x != debug_src_x ||
9021 g_em->src_y != debug_src_y)
9023 static int last_i = -1;
9031 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9032 p, i, element, element_info[element].token_name,
9033 element_action_info[effective_action].suffix, direction);
9035 if (element != effective_element)
9036 printf(" [%d ('%s')]",
9038 element_info[effective_element].token_name);
9042 if (g_em->bitmap != debug_bitmap)
9043 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9044 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9046 if (g_em->src_x != debug_src_x ||
9047 g_em->src_y != debug_src_y)
9048 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9050 g_em->src_x, g_em->src_y,
9051 g_em->src_x / 32, g_em->src_y / 32,
9052 debug_src_x, debug_src_y,
9053 debug_src_x / 32, debug_src_y / 32);
9055 num_em_gfx_errors++;
9065 printf("::: [%d errors found]\n", num_em_gfx_errors);
9071 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9072 boolean any_player_moving,
9073 boolean any_player_snapping,
9074 boolean any_player_dropping)
9076 if (frame == 0 && !any_player_dropping)
9078 if (!local_player->was_waiting)
9080 if (!CheckSaveEngineSnapshotToList())
9083 local_player->was_waiting = TRUE;
9086 else if (any_player_moving || any_player_snapping || any_player_dropping)
9088 local_player->was_waiting = FALSE;
9092 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9093 boolean murphy_is_dropping)
9095 if (murphy_is_waiting)
9097 if (!local_player->was_waiting)
9099 if (!CheckSaveEngineSnapshotToList())
9102 local_player->was_waiting = TRUE;
9107 local_player->was_waiting = FALSE;
9111 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9112 boolean button_released)
9114 if (button_released)
9116 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9117 CheckSaveEngineSnapshotToList();
9119 else if (element_clicked)
9121 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9122 CheckSaveEngineSnapshotToList();
9124 game.snapshot.changed_action = TRUE;
9128 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9129 boolean any_player_moving,
9130 boolean any_player_snapping,
9131 boolean any_player_dropping)
9133 if (tape.single_step && tape.recording && !tape.pausing)
9134 if (frame == 0 && !any_player_dropping)
9135 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9137 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9138 any_player_snapping, any_player_dropping);
9141 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9142 boolean murphy_is_dropping)
9144 boolean murphy_starts_dropping = FALSE;
9147 for (i = 0; i < MAX_PLAYERS; i++)
9148 if (stored_player[i].force_dropping)
9149 murphy_starts_dropping = TRUE;
9151 if (tape.single_step && tape.recording && !tape.pausing)
9152 if (murphy_is_waiting && !murphy_starts_dropping)
9153 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9155 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9158 void CheckSingleStepMode_MM(boolean element_clicked,
9159 boolean button_released)
9161 if (tape.single_step && tape.recording && !tape.pausing)
9162 if (button_released)
9163 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9165 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9168 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9169 int graphic, int sync_frame, int x, int y)
9171 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9173 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9176 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9178 return (IS_NEXT_FRAME(sync_frame, graphic));
9181 int getGraphicInfo_Delay(int graphic)
9183 return graphic_info[graphic].anim_delay;
9186 void PlayMenuSoundExt(int sound)
9188 if (sound == SND_UNDEFINED)
9191 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9192 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9195 if (IS_LOOP_SOUND(sound))
9196 PlaySoundLoop(sound);
9201 void PlayMenuSound(void)
9203 PlayMenuSoundExt(menu.sound[game_status]);
9206 void PlayMenuSoundStereo(int sound, int stereo_position)
9208 if (sound == SND_UNDEFINED)
9211 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9212 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9215 if (IS_LOOP_SOUND(sound))
9216 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9218 PlaySoundStereo(sound, stereo_position);
9221 void PlayMenuSoundIfLoopExt(int sound)
9223 if (sound == SND_UNDEFINED)
9226 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9227 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9230 if (IS_LOOP_SOUND(sound))
9231 PlaySoundLoop(sound);
9234 void PlayMenuSoundIfLoop(void)
9236 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9239 void PlayMenuMusicExt(int music)
9241 if (music == MUS_UNDEFINED)
9244 if (!setup.sound_music)
9247 if (IS_LOOP_MUSIC(music))
9248 PlayMusicLoop(music);
9253 void PlayMenuMusic(void)
9255 char *curr_music = getCurrentlyPlayingMusicFilename();
9256 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9258 if (!strEqual(curr_music, next_music))
9259 PlayMenuMusicExt(menu.music[game_status]);
9262 void PlayMenuSoundsAndMusic(void)
9268 static void FadeMenuSounds(void)
9273 static void FadeMenuMusic(void)
9275 char *curr_music = getCurrentlyPlayingMusicFilename();
9276 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9278 if (!strEqual(curr_music, next_music))
9282 void FadeMenuSoundsAndMusic(void)
9288 void PlaySoundActivating(void)
9291 PlaySound(SND_MENU_ITEM_ACTIVATING);
9295 void PlaySoundSelecting(void)
9298 PlaySound(SND_MENU_ITEM_SELECTING);
9302 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9304 boolean change_fullscreen = (setup.fullscreen !=
9305 video.fullscreen_enabled);
9306 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9307 setup.window_scaling_percent !=
9308 video.window_scaling_percent);
9310 if (change_window_scaling_percent && video.fullscreen_enabled)
9313 if (!change_window_scaling_percent && !video.fullscreen_available)
9316 if (change_window_scaling_percent)
9318 SDLSetWindowScaling(setup.window_scaling_percent);
9322 else if (change_fullscreen)
9324 SDLSetWindowFullscreen(setup.fullscreen);
9326 // set setup value according to successfully changed fullscreen mode
9327 setup.fullscreen = video.fullscreen_enabled;
9332 if (change_fullscreen ||
9333 change_window_scaling_percent)
9335 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9337 // save backbuffer content which gets lost when toggling fullscreen mode
9338 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9340 if (change_window_scaling_percent)
9342 // keep window mode, but change window scaling
9343 video.fullscreen_enabled = TRUE; // force new window scaling
9346 // toggle fullscreen
9347 ChangeVideoModeIfNeeded(setup.fullscreen);
9349 // set setup value according to successfully changed fullscreen mode
9350 setup.fullscreen = video.fullscreen_enabled;
9352 // restore backbuffer content from temporary backbuffer backup bitmap
9353 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9355 FreeBitmap(tmp_backbuffer);
9357 // update visible window/screen
9358 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9362 static void JoinRectangles(int *x, int *y, int *width, int *height,
9363 int x2, int y2, int width2, int height2)
9365 // do not join with "off-screen" rectangle
9366 if (x2 == -1 || y2 == -1)
9371 *width = MAX(*width, width2);
9372 *height = MAX(*height, height2);
9375 void SetAnimStatus(int anim_status_new)
9377 if (anim_status_new == GAME_MODE_MAIN)
9378 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9379 else if (anim_status_new == GAME_MODE_SCORES)
9380 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9382 global.anim_status_next = anim_status_new;
9384 // directly set screen modes that are entered without fading
9385 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9386 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9387 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9388 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9389 global.anim_status = global.anim_status_next;
9392 void SetGameStatus(int game_status_new)
9394 if (game_status_new != game_status)
9395 game_status_last_screen = game_status;
9397 game_status = game_status_new;
9399 SetAnimStatus(game_status_new);
9402 void SetFontStatus(int game_status_new)
9404 static int last_game_status = -1;
9406 if (game_status_new != -1)
9408 // set game status for font use after storing last game status
9409 last_game_status = game_status;
9410 game_status = game_status_new;
9414 // reset game status after font use from last stored game status
9415 game_status = last_game_status;
9419 void ResetFontStatus(void)
9424 void SetLevelSetInfo(char *identifier, int level_nr)
9426 setString(&levelset.identifier, identifier);
9428 levelset.level_nr = level_nr;
9431 boolean CheckIfAllViewportsHaveChanged(void)
9433 // if game status has not changed, viewports have not changed either
9434 if (game_status == game_status_last)
9437 // check if all viewports have changed with current game status
9439 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9440 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9441 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9442 int new_real_sx = vp_playfield->x;
9443 int new_real_sy = vp_playfield->y;
9444 int new_full_sxsize = vp_playfield->width;
9445 int new_full_sysize = vp_playfield->height;
9446 int new_dx = vp_door_1->x;
9447 int new_dy = vp_door_1->y;
9448 int new_dxsize = vp_door_1->width;
9449 int new_dysize = vp_door_1->height;
9450 int new_vx = vp_door_2->x;
9451 int new_vy = vp_door_2->y;
9452 int new_vxsize = vp_door_2->width;
9453 int new_vysize = vp_door_2->height;
9455 boolean playfield_viewport_has_changed =
9456 (new_real_sx != REAL_SX ||
9457 new_real_sy != REAL_SY ||
9458 new_full_sxsize != FULL_SXSIZE ||
9459 new_full_sysize != FULL_SYSIZE);
9461 boolean door_1_viewport_has_changed =
9464 new_dxsize != DXSIZE ||
9465 new_dysize != DYSIZE);
9467 boolean door_2_viewport_has_changed =
9470 new_vxsize != VXSIZE ||
9471 new_vysize != VYSIZE ||
9472 game_status_last == GAME_MODE_EDITOR);
9474 return (playfield_viewport_has_changed &&
9475 door_1_viewport_has_changed &&
9476 door_2_viewport_has_changed);
9479 boolean CheckFadeAll(void)
9481 return (CheckIfGlobalBorderHasChanged() ||
9482 CheckIfAllViewportsHaveChanged());
9485 void ChangeViewportPropertiesIfNeeded(void)
9487 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9488 FALSE : setup.small_game_graphics);
9489 int gfx_game_mode = game_status;
9490 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9492 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9493 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9494 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9495 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9496 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9497 int new_win_xsize = vp_window->width;
9498 int new_win_ysize = vp_window->height;
9499 int border_left = vp_playfield->border_left;
9500 int border_right = vp_playfield->border_right;
9501 int border_top = vp_playfield->border_top;
9502 int border_bottom = vp_playfield->border_bottom;
9503 int new_sx = vp_playfield->x + border_left;
9504 int new_sy = vp_playfield->y + border_top;
9505 int new_sxsize = vp_playfield->width - border_left - border_right;
9506 int new_sysize = vp_playfield->height - border_top - border_bottom;
9507 int new_real_sx = vp_playfield->x;
9508 int new_real_sy = vp_playfield->y;
9509 int new_full_sxsize = vp_playfield->width;
9510 int new_full_sysize = vp_playfield->height;
9511 int new_dx = vp_door_1->x;
9512 int new_dy = vp_door_1->y;
9513 int new_dxsize = vp_door_1->width;
9514 int new_dysize = vp_door_1->height;
9515 int new_vx = vp_door_2->x;
9516 int new_vy = vp_door_2->y;
9517 int new_vxsize = vp_door_2->width;
9518 int new_vysize = vp_door_2->height;
9519 int new_ex = vp_door_3->x;
9520 int new_ey = vp_door_3->y;
9521 int new_exsize = vp_door_3->width;
9522 int new_eysize = vp_door_3->height;
9523 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9524 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9525 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9526 int new_scr_fieldx = new_sxsize / tilesize;
9527 int new_scr_fieldy = new_sysize / tilesize;
9528 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9529 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9530 boolean init_gfx_buffers = FALSE;
9531 boolean init_video_buffer = FALSE;
9532 boolean init_gadgets_and_anims = FALSE;
9533 boolean init_em_graphics = FALSE;
9535 if (new_win_xsize != WIN_XSIZE ||
9536 new_win_ysize != WIN_YSIZE)
9538 WIN_XSIZE = new_win_xsize;
9539 WIN_YSIZE = new_win_ysize;
9541 init_video_buffer = TRUE;
9542 init_gfx_buffers = TRUE;
9543 init_gadgets_and_anims = TRUE;
9545 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9548 if (new_scr_fieldx != SCR_FIELDX ||
9549 new_scr_fieldy != SCR_FIELDY)
9551 // this always toggles between MAIN and GAME when using small tile size
9553 SCR_FIELDX = new_scr_fieldx;
9554 SCR_FIELDY = new_scr_fieldy;
9556 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9567 new_sxsize != SXSIZE ||
9568 new_sysize != SYSIZE ||
9569 new_dxsize != DXSIZE ||
9570 new_dysize != DYSIZE ||
9571 new_vxsize != VXSIZE ||
9572 new_vysize != VYSIZE ||
9573 new_exsize != EXSIZE ||
9574 new_eysize != EYSIZE ||
9575 new_real_sx != REAL_SX ||
9576 new_real_sy != REAL_SY ||
9577 new_full_sxsize != FULL_SXSIZE ||
9578 new_full_sysize != FULL_SYSIZE ||
9579 new_tilesize_var != TILESIZE_VAR
9582 // ------------------------------------------------------------------------
9583 // determine next fading area for changed viewport definitions
9584 // ------------------------------------------------------------------------
9586 // start with current playfield area (default fading area)
9589 FADE_SXSIZE = FULL_SXSIZE;
9590 FADE_SYSIZE = FULL_SYSIZE;
9592 // add new playfield area if position or size has changed
9593 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9594 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9596 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9597 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9600 // add current and new door 1 area if position or size has changed
9601 if (new_dx != DX || new_dy != DY ||
9602 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9604 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9605 DX, DY, DXSIZE, DYSIZE);
9606 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9607 new_dx, new_dy, new_dxsize, new_dysize);
9610 // add current and new door 2 area if position or size has changed
9611 if (new_vx != VX || new_vy != VY ||
9612 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9614 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9615 VX, VY, VXSIZE, VYSIZE);
9616 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9617 new_vx, new_vy, new_vxsize, new_vysize);
9620 // ------------------------------------------------------------------------
9621 // handle changed tile size
9622 // ------------------------------------------------------------------------
9624 if (new_tilesize_var != TILESIZE_VAR)
9626 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9628 // changing tile size invalidates scroll values of engine snapshots
9629 FreeEngineSnapshotSingle();
9631 // changing tile size requires update of graphic mapping for EM engine
9632 init_em_graphics = TRUE;
9643 SXSIZE = new_sxsize;
9644 SYSIZE = new_sysize;
9645 DXSIZE = new_dxsize;
9646 DYSIZE = new_dysize;
9647 VXSIZE = new_vxsize;
9648 VYSIZE = new_vysize;
9649 EXSIZE = new_exsize;
9650 EYSIZE = new_eysize;
9651 REAL_SX = new_real_sx;
9652 REAL_SY = new_real_sy;
9653 FULL_SXSIZE = new_full_sxsize;
9654 FULL_SYSIZE = new_full_sysize;
9655 TILESIZE_VAR = new_tilesize_var;
9657 init_gfx_buffers = TRUE;
9658 init_gadgets_and_anims = TRUE;
9660 // printf("::: viewports: init_gfx_buffers\n");
9661 // printf("::: viewports: init_gadgets_and_anims\n");
9664 if (init_gfx_buffers)
9666 // printf("::: init_gfx_buffers\n");
9668 SCR_FIELDX = new_scr_fieldx_buffers;
9669 SCR_FIELDY = new_scr_fieldy_buffers;
9673 SCR_FIELDX = new_scr_fieldx;
9674 SCR_FIELDY = new_scr_fieldy;
9676 SetDrawDeactivationMask(REDRAW_NONE);
9677 SetDrawBackgroundMask(REDRAW_FIELD);
9680 if (init_video_buffer)
9682 // printf("::: init_video_buffer\n");
9684 FreeAllImageTextures(); // needs old renderer to free the textures
9686 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9687 InitImageTextures();
9690 if (init_gadgets_and_anims)
9692 // printf("::: init_gadgets_and_anims\n");
9695 InitGlobalAnimations();
9698 if (init_em_graphics)
9700 InitGraphicInfo_EM();