1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 // select level set with EMC X11 graphics before activating EM GFX debugging
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 // tool button identifiers
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 // constants for number of doors and door parts
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 // forward declaration for internal use
169 static void UnmapToolButtons(void);
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 int getFieldbufferOffsetX_RND(int dir, int pos)
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (dir & MV_HORIZONTAL ? pos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
221 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
223 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
224 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
226 fx += (dx_var > 0 ? TILEX_VAR : 0);
233 if (full_lev_fieldx <= SCR_FIELDX)
235 if (EVEN(SCR_FIELDX))
236 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
238 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
244 int getFieldbufferOffsetY_RND(int dir, int pos)
246 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
247 int dy = (dir & MV_VERTICAL ? pos : 0);
248 int dy_var = dy * TILESIZE_VAR / TILESIZE;
251 if (EVEN(SCR_FIELDY))
253 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
254 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
256 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
257 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
259 fy += (dy_var > 0 ? TILEY_VAR : 0);
266 if (full_lev_fieldy <= SCR_FIELDY)
268 if (EVEN(SCR_FIELDY))
269 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
271 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
277 static int getLevelFromScreenX_RND(int sx)
279 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
282 int lx = LEVELX((px + dx) / TILESIZE_VAR);
287 static int getLevelFromScreenY_RND(int sy)
289 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
292 int ly = LEVELY((py + dy) / TILESIZE_VAR);
297 static int getLevelFromScreenX_EM(int sx)
299 int level_xsize = level.native_em_level->lev->width;
300 int full_xsize = level_xsize * TILESIZE_VAR;
302 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
304 int fx = getFieldbufferOffsetX_EM();
307 int lx = LEVELX((px + dx) / TILESIZE_VAR);
309 lx = correctLevelPosX_EM(lx);
314 static int getLevelFromScreenY_EM(int sy)
316 int level_ysize = level.native_em_level->lev->height;
317 int full_ysize = level_ysize * TILESIZE_VAR;
319 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
321 int fy = getFieldbufferOffsetY_EM();
324 int ly = LEVELY((py + dy) / TILESIZE_VAR);
326 ly = correctLevelPosY_EM(ly);
331 static int getLevelFromScreenX_SP(int sx)
333 int menBorder = setup.sp_show_border_elements;
334 int level_xsize = level.native_sp_level->width;
335 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
337 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
339 int fx = getFieldbufferOffsetX_SP();
342 int lx = LEVELX((px + dx) / TILESIZE_VAR);
347 static int getLevelFromScreenY_SP(int sy)
349 int menBorder = setup.sp_show_border_elements;
350 int level_ysize = level.native_sp_level->height;
351 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
353 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
355 int fy = getFieldbufferOffsetY_SP();
358 int ly = LEVELY((py + dy) / TILESIZE_VAR);
363 static int getLevelFromScreenX_MM(int sx)
365 int level_xsize = level.native_mm_level->fieldx;
366 int full_xsize = level_xsize * TILESIZE_VAR;
368 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
371 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
376 static int getLevelFromScreenY_MM(int sy)
378 int level_ysize = level.native_mm_level->fieldy;
379 int full_ysize = level_ysize * TILESIZE_VAR;
381 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
384 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
389 int getLevelFromScreenX(int x)
391 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
392 return getLevelFromScreenX_EM(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
394 return getLevelFromScreenX_SP(x);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
396 return getLevelFromScreenX_MM(x);
398 return getLevelFromScreenX_RND(x);
401 int getLevelFromScreenY(int y)
403 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
404 return getLevelFromScreenY_EM(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
406 return getLevelFromScreenY_SP(y);
407 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
408 return getLevelFromScreenY_MM(y);
410 return getLevelFromScreenY_RND(y);
413 void DumpTile(int x, int y)
419 printf_line("-", 79);
420 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
421 printf_line("-", 79);
423 if (!IN_LEV_FIELD(x, y))
425 printf("(not in level field)\n");
431 token_name = element_info[Feld[x][y]].token_name;
433 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
434 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
435 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
436 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
437 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
438 printf(" MovPos: %d\n", MovPos[x][y]);
439 printf(" MovDir: %d\n", MovDir[x][y]);
440 printf(" MovDelay: %d\n", MovDelay[x][y]);
441 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
442 printf(" CustomValue: %d\n", CustomValue[x][y]);
443 printf(" GfxElement: %d\n", GfxElement[x][y]);
444 printf(" GfxAction: %d\n", GfxAction[x][y]);
445 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
446 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
450 void DumpTileFromScreen(int sx, int sy)
452 int lx = getLevelFromScreenX(sx);
453 int ly = getLevelFromScreenY(sy);
458 void SetDrawtoField(int mode)
460 if (mode == DRAW_TO_FIELDBUFFER)
466 BX2 = SCR_FIELDX + 1;
467 BY2 = SCR_FIELDY + 1;
469 drawto_field = fieldbuffer;
471 else // DRAW_TO_BACKBUFFER
477 BX2 = SCR_FIELDX - 1;
478 BY2 = SCR_FIELDY - 1;
480 drawto_field = backbuffer;
484 static void RedrawPlayfield_RND(void)
486 if (game.envelope_active)
489 DrawLevel(REDRAW_ALL);
493 void RedrawPlayfield(void)
495 if (game_status != GAME_MODE_PLAYING)
498 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
499 RedrawPlayfield_EM(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
501 RedrawPlayfield_SP(TRUE);
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
503 RedrawPlayfield_MM();
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
505 RedrawPlayfield_RND();
507 BlitScreenToBitmap(backbuffer);
509 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
513 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
516 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
517 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
519 if (x == -1 && y == -1)
522 if (draw_target == DRAW_TO_SCREEN)
523 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
525 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
528 static void DrawMaskedBorderExt_FIELD(int draw_target)
530 if (global.border_status >= GAME_MODE_MAIN &&
531 global.border_status <= GAME_MODE_PLAYING &&
532 border.draw_masked[global.border_status])
533 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
537 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
539 // when drawing to backbuffer, never draw border over open doors
540 if (draw_target == DRAW_TO_BACKBUFFER &&
541 (GetDoorState() & DOOR_OPEN_1))
544 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
545 (global.border_status != GAME_MODE_EDITOR ||
546 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
547 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
550 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
552 // when drawing to backbuffer, never draw border over open doors
553 if (draw_target == DRAW_TO_BACKBUFFER &&
554 (GetDoorState() & DOOR_OPEN_2))
557 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
558 global.border_status != GAME_MODE_EDITOR)
559 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
562 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
564 // currently not available
567 static void DrawMaskedBorderExt_ALL(int draw_target)
569 DrawMaskedBorderExt_FIELD(draw_target);
570 DrawMaskedBorderExt_DOOR_1(draw_target);
571 DrawMaskedBorderExt_DOOR_2(draw_target);
572 DrawMaskedBorderExt_DOOR_3(draw_target);
575 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
577 // never draw masked screen borders on borderless screens
578 if (global.border_status == GAME_MODE_LOADING ||
579 global.border_status == GAME_MODE_TITLE)
582 if (redraw_mask & REDRAW_ALL)
583 DrawMaskedBorderExt_ALL(draw_target);
586 if (redraw_mask & REDRAW_FIELD)
587 DrawMaskedBorderExt_FIELD(draw_target);
588 if (redraw_mask & REDRAW_DOOR_1)
589 DrawMaskedBorderExt_DOOR_1(draw_target);
590 if (redraw_mask & REDRAW_DOOR_2)
591 DrawMaskedBorderExt_DOOR_2(draw_target);
592 if (redraw_mask & REDRAW_DOOR_3)
593 DrawMaskedBorderExt_DOOR_3(draw_target);
597 void DrawMaskedBorder_FIELD(void)
599 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
602 void DrawMaskedBorder(int redraw_mask)
604 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
607 void DrawMaskedBorderToTarget(int draw_target)
609 if (draw_target == DRAW_TO_BACKBUFFER ||
610 draw_target == DRAW_TO_SCREEN)
612 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
616 int last_border_status = global.border_status;
618 if (draw_target == DRAW_TO_FADE_SOURCE)
620 global.border_status = gfx.fade_border_source_status;
621 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
623 else if (draw_target == DRAW_TO_FADE_TARGET)
625 global.border_status = gfx.fade_border_target_status;
626 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
629 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
631 global.border_status = last_border_status;
632 gfx.masked_border_bitmap_ptr = backbuffer;
636 void DrawTileCursor(int draw_target)
642 int graphic = IMG_GLOBAL_TILE_CURSOR;
644 int tilesize = TILESIZE_VAR;
645 int width = tilesize;
646 int height = tilesize;
648 if (game_status != GAME_MODE_PLAYING)
651 if (!tile_cursor.enabled ||
655 if (tile_cursor.moving)
657 int step = TILESIZE_VAR / 4;
658 int dx = tile_cursor.target_x - tile_cursor.x;
659 int dy = tile_cursor.target_y - tile_cursor.y;
662 tile_cursor.x = tile_cursor.target_x;
664 tile_cursor.x += SIGN(dx) * step;
667 tile_cursor.y = tile_cursor.target_y;
669 tile_cursor.y += SIGN(dy) * step;
671 if (tile_cursor.x == tile_cursor.target_x &&
672 tile_cursor.y == tile_cursor.target_y)
673 tile_cursor.moving = FALSE;
676 dst_x = tile_cursor.x;
677 dst_y = tile_cursor.y;
679 frame = getGraphicAnimationFrame(graphic, -1);
681 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
684 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
685 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
687 if (draw_target == DRAW_TO_SCREEN)
688 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
690 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
694 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
696 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
699 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
701 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
702 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
704 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
707 void BlitScreenToBitmap(Bitmap *target_bitmap)
709 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
710 BlitScreenToBitmap_EM(target_bitmap);
711 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
712 BlitScreenToBitmap_SP(target_bitmap);
713 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
714 BlitScreenToBitmap_MM(target_bitmap);
715 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
716 BlitScreenToBitmap_RND(target_bitmap);
718 redraw_mask |= REDRAW_FIELD;
721 static void DrawFramesPerSecond(void)
724 int font_nr = FONT_TEXT_2;
725 int font_width = getFontWidth(font_nr);
726 int draw_deactivation_mask = GetDrawDeactivationMask();
727 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
729 // draw FPS with leading space (needed if field buffer deactivated)
730 sprintf(text, " %04.1f fps", global.frames_per_second);
732 // override draw deactivation mask (required for invisible warp mode)
733 SetDrawDeactivationMask(REDRAW_NONE);
735 // draw opaque FPS if field buffer deactivated, else draw masked FPS
736 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
737 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
739 // set draw deactivation mask to previous value
740 SetDrawDeactivationMask(draw_deactivation_mask);
742 // force full-screen redraw in this frame
743 redraw_mask = REDRAW_ALL;
747 static void PrintFrameTimeDebugging(void)
749 static unsigned int last_counter = 0;
750 unsigned int counter = Counter();
751 int diff_1 = counter - last_counter;
752 int diff_2 = diff_1 - GAME_FRAME_DELAY;
754 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
755 char diff_bar[2 * diff_2_max + 5];
759 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
761 for (i = 0; i < diff_2_max; i++)
762 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
763 i >= diff_2_max - diff_2_cut ? '-' : ' ');
765 diff_bar[pos++] = '|';
767 for (i = 0; i < diff_2_max; i++)
768 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
770 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
772 diff_bar[pos++] = '\0';
774 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
777 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
780 last_counter = counter;
784 static int unifiedRedrawMask(int mask)
786 if (mask & REDRAW_ALL)
789 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
795 static boolean equalRedrawMasks(int mask_1, int mask_2)
797 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
800 void BackToFront(void)
802 static int last_redraw_mask = REDRAW_NONE;
804 // force screen redraw in every frame to continue drawing global animations
805 // (but always use the last redraw mask to prevent unwanted side effects)
806 if (redraw_mask == REDRAW_NONE)
807 redraw_mask = last_redraw_mask;
809 last_redraw_mask = redraw_mask;
812 // masked border now drawn immediately when blitting backbuffer to window
814 // draw masked border to all viewports, if defined
815 DrawMaskedBorder(redraw_mask);
818 // draw frames per second (only if debug mode is enabled)
819 if (redraw_mask & REDRAW_FPS)
820 DrawFramesPerSecond();
822 // remove playfield redraw before potentially merging with doors redraw
823 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
824 redraw_mask &= ~REDRAW_FIELD;
826 // redraw complete window if both playfield and (some) doors need redraw
827 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
828 redraw_mask = REDRAW_ALL;
830 /* although redrawing the whole window would be fine for normal gameplay,
831 being able to only redraw the playfield is required for deactivating
832 certain drawing areas (mainly playfield) to work, which is needed for
833 warp-forward to be fast enough (by skipping redraw of most frames) */
835 if (redraw_mask & REDRAW_ALL)
837 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
839 else if (redraw_mask & REDRAW_FIELD)
841 BlitBitmap(backbuffer, window,
842 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
844 else if (redraw_mask & REDRAW_DOORS)
846 // merge door areas to prevent calling screen redraw more than once
852 if (redraw_mask & REDRAW_DOOR_1)
856 x2 = MAX(x2, DX + DXSIZE);
857 y2 = MAX(y2, DY + DYSIZE);
860 if (redraw_mask & REDRAW_DOOR_2)
864 x2 = MAX(x2, VX + VXSIZE);
865 y2 = MAX(y2, VY + VYSIZE);
868 if (redraw_mask & REDRAW_DOOR_3)
872 x2 = MAX(x2, EX + EXSIZE);
873 y2 = MAX(y2, EY + EYSIZE);
876 // make sure that at least one pixel is blitted, and inside the screen
877 // (else nothing is blitted, causing the animations not to be updated)
878 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
879 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
880 x2 = MIN(MAX(1, x2), WIN_XSIZE);
881 y2 = MIN(MAX(1, y2), WIN_YSIZE);
883 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
886 redraw_mask = REDRAW_NONE;
889 PrintFrameTimeDebugging();
893 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
895 unsigned int frame_delay_value_old = GetVideoFrameDelay();
897 SetVideoFrameDelay(frame_delay_value);
901 SetVideoFrameDelay(frame_delay_value_old);
904 static int fade_type_skip = FADE_TYPE_NONE;
906 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
908 void (*draw_border_function)(void) = NULL;
909 int x, y, width, height;
910 int fade_delay, post_delay;
912 if (fade_type == FADE_TYPE_FADE_OUT)
914 if (fade_type_skip != FADE_TYPE_NONE)
916 // skip all fade operations until specified fade operation
917 if (fade_type & fade_type_skip)
918 fade_type_skip = FADE_TYPE_NONE;
923 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
927 redraw_mask |= fade_mask;
929 if (fade_type == FADE_TYPE_SKIP)
931 fade_type_skip = fade_mode;
936 fade_delay = fading.fade_delay;
937 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
939 if (fade_type_skip != FADE_TYPE_NONE)
941 // skip all fade operations until specified fade operation
942 if (fade_type & fade_type_skip)
943 fade_type_skip = FADE_TYPE_NONE;
948 if (global.autoplay_leveldir)
953 if (fade_mask == REDRAW_FIELD)
958 height = FADE_SYSIZE;
960 if (border.draw_masked_when_fading)
961 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
963 DrawMaskedBorder_FIELD(); // draw once
973 if (!setup.fade_screens ||
975 fading.fade_mode == FADE_MODE_NONE)
977 if (fade_mode == FADE_MODE_FADE_OUT)
980 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
982 redraw_mask &= ~fade_mask;
987 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
988 draw_border_function);
990 redraw_mask &= ~fade_mask;
992 ClearAutoRepeatKeyEvents();
995 static void SetScreenStates_BeforeFadingIn(void)
997 // temporarily set screen mode for animations to screen after fading in
998 global.anim_status = global.anim_status_next;
1000 // store backbuffer with all animations that will be started after fading in
1001 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
1002 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
1004 // set screen mode for animations back to fading
1005 global.anim_status = GAME_MODE_PSEUDO_FADING;
1008 static void SetScreenStates_AfterFadingIn(void)
1010 // store new source screen (to use correct masked border for fading)
1011 gfx.fade_border_source_status = global.border_status;
1013 global.anim_status = global.anim_status_next;
1016 static void SetScreenStates_BeforeFadingOut(void)
1018 // store new target screen (to use correct masked border for fading)
1019 gfx.fade_border_target_status = game_status;
1021 // set screen mode for animations to fading
1022 global.anim_status = GAME_MODE_PSEUDO_FADING;
1024 // store backbuffer with all animations that will be stopped for fading out
1025 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1026 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1029 static void SetScreenStates_AfterFadingOut(void)
1031 global.border_status = game_status;
1034 void FadeIn(int fade_mask)
1036 SetScreenStates_BeforeFadingIn();
1039 DrawMaskedBorder(REDRAW_ALL);
1042 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1043 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1045 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1049 FADE_SXSIZE = FULL_SXSIZE;
1050 FADE_SYSIZE = FULL_SYSIZE;
1052 // activate virtual buttons depending on upcoming game status
1053 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1054 game_status == GAME_MODE_PLAYING && !tape.playing)
1055 SetOverlayActive(TRUE);
1057 SetScreenStates_AfterFadingIn();
1059 // force update of global animation status in case of rapid screen changes
1060 redraw_mask = REDRAW_ALL;
1064 void FadeOut(int fade_mask)
1066 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1067 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1068 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1071 SetScreenStates_BeforeFadingOut();
1073 SetTileCursorActive(FALSE);
1074 SetOverlayActive(FALSE);
1077 DrawMaskedBorder(REDRAW_ALL);
1080 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1081 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1083 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1085 SetScreenStates_AfterFadingOut();
1088 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1090 static struct TitleFadingInfo fading_leave_stored;
1093 fading_leave_stored = fading_leave;
1095 fading = fading_leave_stored;
1098 void FadeSetEnterMenu(void)
1100 fading = menu.enter_menu;
1102 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1105 void FadeSetLeaveMenu(void)
1107 fading = menu.leave_menu;
1109 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1112 void FadeSetEnterScreen(void)
1114 fading = menu.enter_screen[game_status];
1116 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1119 void FadeSetNextScreen(void)
1121 fading = menu.next_screen[game_status];
1123 // (do not overwrite fade mode set by FadeSetEnterScreen)
1124 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1127 void FadeSetLeaveScreen(void)
1129 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1132 void FadeSetFromType(int type)
1134 if (type & TYPE_ENTER_SCREEN)
1135 FadeSetEnterScreen();
1136 else if (type & TYPE_ENTER)
1138 else if (type & TYPE_LEAVE)
1142 void FadeSetDisabled(void)
1144 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1146 fading = fading_none;
1149 void FadeSkipNextFadeIn(void)
1151 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1154 void FadeSkipNextFadeOut(void)
1156 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1159 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1161 if (graphic == IMG_UNDEFINED)
1164 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1166 return (graphic_info[graphic].bitmap != NULL || redefined ?
1167 graphic_info[graphic].bitmap :
1168 graphic_info[default_graphic].bitmap);
1171 static Bitmap *getBackgroundBitmap(int graphic)
1173 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1176 static Bitmap *getGlobalBorderBitmap(int graphic)
1178 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1181 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1184 (status == GAME_MODE_MAIN ||
1185 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1186 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1187 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1188 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1191 return getGlobalBorderBitmap(graphic);
1194 void SetWindowBackgroundImageIfDefined(int graphic)
1196 if (graphic_info[graphic].bitmap)
1197 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1200 void SetMainBackgroundImageIfDefined(int graphic)
1202 if (graphic_info[graphic].bitmap)
1203 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1206 void SetDoorBackgroundImageIfDefined(int graphic)
1208 if (graphic_info[graphic].bitmap)
1209 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1212 void SetWindowBackgroundImage(int graphic)
1214 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1217 void SetMainBackgroundImage(int graphic)
1219 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1222 void SetDoorBackgroundImage(int graphic)
1224 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1227 void SetPanelBackground(void)
1229 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1231 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1232 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1234 SetDoorBackgroundBitmap(bitmap_db_panel);
1237 void DrawBackground(int x, int y, int width, int height)
1239 // "drawto" might still point to playfield buffer here (hall of fame)
1240 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1242 if (IN_GFX_FIELD_FULL(x, y))
1243 redraw_mask |= REDRAW_FIELD;
1244 else if (IN_GFX_DOOR_1(x, y))
1245 redraw_mask |= REDRAW_DOOR_1;
1246 else if (IN_GFX_DOOR_2(x, y))
1247 redraw_mask |= REDRAW_DOOR_2;
1248 else if (IN_GFX_DOOR_3(x, y))
1249 redraw_mask |= REDRAW_DOOR_3;
1252 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1254 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1256 if (font->bitmap == NULL)
1259 DrawBackground(x, y, width, height);
1262 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1264 struct GraphicInfo *g = &graphic_info[graphic];
1266 if (g->bitmap == NULL)
1269 DrawBackground(x, y, width, height);
1272 static int game_status_last = -1;
1273 static Bitmap *global_border_bitmap_last = NULL;
1274 static Bitmap *global_border_bitmap = NULL;
1275 static int real_sx_last = -1, real_sy_last = -1;
1276 static int full_sxsize_last = -1, full_sysize_last = -1;
1277 static int dx_last = -1, dy_last = -1;
1278 static int dxsize_last = -1, dysize_last = -1;
1279 static int vx_last = -1, vy_last = -1;
1280 static int vxsize_last = -1, vysize_last = -1;
1281 static int ex_last = -1, ey_last = -1;
1282 static int exsize_last = -1, eysize_last = -1;
1284 boolean CheckIfGlobalBorderHasChanged(void)
1286 // if game status has not changed, global border has not changed either
1287 if (game_status == game_status_last)
1290 // determine and store new global border bitmap for current game status
1291 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1293 return (global_border_bitmap_last != global_border_bitmap);
1296 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1298 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1299 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1301 // if game status has not changed, nothing has to be redrawn
1302 if (game_status == game_status_last)
1305 // redraw if last screen was title screen
1306 if (game_status_last == GAME_MODE_TITLE)
1309 // redraw if global screen border has changed
1310 if (CheckIfGlobalBorderHasChanged())
1313 // redraw if position or size of playfield area has changed
1314 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1315 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1318 // redraw if position or size of door area has changed
1319 if (dx_last != DX || dy_last != DY ||
1320 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1323 // redraw if position or size of tape area has changed
1324 if (vx_last != VX || vy_last != VY ||
1325 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1328 // redraw if position or size of editor area has changed
1329 if (ex_last != EX || ey_last != EY ||
1330 exsize_last != EXSIZE || eysize_last != EYSIZE)
1337 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1340 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1342 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1345 void RedrawGlobalBorder(void)
1347 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1349 RedrawGlobalBorderFromBitmap(bitmap);
1351 redraw_mask = REDRAW_ALL;
1354 static void RedrawGlobalBorderIfNeeded(void)
1356 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1357 if (game_status == game_status_last)
1361 // copy current draw buffer to later copy back areas that have not changed
1362 if (game_status_last != GAME_MODE_TITLE)
1363 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1365 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1366 if (CheckIfGlobalBorderRedrawIsNeeded())
1369 // redraw global screen border (or clear, if defined to be empty)
1370 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1372 if (game_status == GAME_MODE_EDITOR)
1373 DrawSpecialEditorDoor();
1375 // copy previous playfield and door areas, if they are defined on both
1376 // previous and current screen and if they still have the same size
1378 if (real_sx_last != -1 && real_sy_last != -1 &&
1379 REAL_SX != -1 && REAL_SY != -1 &&
1380 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1381 BlitBitmap(bitmap_db_store_1, backbuffer,
1382 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1385 if (dx_last != -1 && dy_last != -1 &&
1386 DX != -1 && DY != -1 &&
1387 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1388 BlitBitmap(bitmap_db_store_1, backbuffer,
1389 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1391 if (game_status != GAME_MODE_EDITOR)
1393 if (vx_last != -1 && vy_last != -1 &&
1394 VX != -1 && VY != -1 &&
1395 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1396 BlitBitmap(bitmap_db_store_1, backbuffer,
1397 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1401 if (ex_last != -1 && ey_last != -1 &&
1402 EX != -1 && EY != -1 &&
1403 exsize_last == EXSIZE && eysize_last == EYSIZE)
1404 BlitBitmap(bitmap_db_store_1, backbuffer,
1405 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1408 redraw_mask = REDRAW_ALL;
1411 game_status_last = game_status;
1413 global_border_bitmap_last = global_border_bitmap;
1415 real_sx_last = REAL_SX;
1416 real_sy_last = REAL_SY;
1417 full_sxsize_last = FULL_SXSIZE;
1418 full_sysize_last = FULL_SYSIZE;
1421 dxsize_last = DXSIZE;
1422 dysize_last = DYSIZE;
1425 vxsize_last = VXSIZE;
1426 vysize_last = VYSIZE;
1429 exsize_last = EXSIZE;
1430 eysize_last = EYSIZE;
1433 void ClearField(void)
1435 RedrawGlobalBorderIfNeeded();
1437 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1438 // (when entering hall of fame after playing)
1439 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1441 // !!! maybe this should be done before clearing the background !!!
1442 if (game_status == GAME_MODE_PLAYING)
1444 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1445 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1449 SetDrawtoField(DRAW_TO_BACKBUFFER);
1453 void MarkTileDirty(int x, int y)
1455 redraw_mask |= REDRAW_FIELD;
1458 void SetBorderElement(void)
1462 BorderElement = EL_EMPTY;
1464 // the MM game engine does not use a visible border element
1465 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1468 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1470 for (x = 0; x < lev_fieldx; x++)
1472 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1473 BorderElement = EL_STEELWALL;
1475 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1481 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1482 int max_array_fieldx, int max_array_fieldy,
1483 short field[max_array_fieldx][max_array_fieldy],
1484 int max_fieldx, int max_fieldy)
1488 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1489 static int safety = 0;
1491 // check if starting field still has the desired content
1492 if (field[from_x][from_y] == fill_element)
1497 if (safety > max_fieldx * max_fieldy)
1498 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1500 old_element = field[from_x][from_y];
1501 field[from_x][from_y] = fill_element;
1503 for (i = 0; i < 4; i++)
1505 x = from_x + check[i][0];
1506 y = from_y + check[i][1];
1508 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1509 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1510 field, max_fieldx, max_fieldy);
1516 void FloodFillLevel(int from_x, int from_y, int fill_element,
1517 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1518 int max_fieldx, int max_fieldy)
1520 FloodFillLevelExt(from_x, from_y, fill_element,
1521 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1522 max_fieldx, max_fieldy);
1525 void SetRandomAnimationValue(int x, int y)
1527 gfx.anim_random_frame = GfxRandom[x][y];
1530 int getGraphicAnimationFrame(int graphic, int sync_frame)
1532 // animation synchronized with global frame counter, not move position
1533 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1534 sync_frame = FrameCounter;
1536 return getAnimationFrame(graphic_info[graphic].anim_frames,
1537 graphic_info[graphic].anim_delay,
1538 graphic_info[graphic].anim_mode,
1539 graphic_info[graphic].anim_start_frame,
1543 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1545 struct GraphicInfo *g = &graphic_info[graphic];
1546 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1548 if (tilesize == gfx.standard_tile_size)
1549 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1550 else if (tilesize == game.tile_size)
1551 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1553 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1556 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1557 boolean get_backside)
1559 struct GraphicInfo *g = &graphic_info[graphic];
1560 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1561 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1563 if (g->offset_y == 0) // frames are ordered horizontally
1565 int max_width = g->anim_frames_per_line * g->width;
1566 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1568 *x = pos % max_width;
1569 *y = src_y % g->height + pos / max_width * g->height;
1571 else if (g->offset_x == 0) // frames are ordered vertically
1573 int max_height = g->anim_frames_per_line * g->height;
1574 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1576 *x = src_x % g->width + pos / max_height * g->width;
1577 *y = pos % max_height;
1579 else // frames are ordered diagonally
1581 *x = src_x + frame * g->offset_x;
1582 *y = src_y + frame * g->offset_y;
1586 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1587 Bitmap **bitmap, int *x, int *y,
1588 boolean get_backside)
1590 struct GraphicInfo *g = &graphic_info[graphic];
1592 // if no graphics defined at all, use fallback graphics
1593 if (g->bitmaps == NULL)
1594 *g = graphic_info[IMG_CHAR_EXCLAM];
1596 // if no in-game graphics defined, always use standard graphic size
1597 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1598 tilesize = TILESIZE;
1600 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1601 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1603 *x = *x * tilesize / g->tile_size;
1604 *y = *y * tilesize / g->tile_size;
1607 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1608 Bitmap **bitmap, int *x, int *y)
1610 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1613 void getFixedGraphicSource(int graphic, int frame,
1614 Bitmap **bitmap, int *x, int *y)
1616 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1619 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1621 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1624 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1625 int *x, int *y, boolean get_backside)
1627 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1631 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1633 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1636 void DrawGraphic(int x, int y, int graphic, int frame)
1639 if (!IN_SCR_FIELD(x, y))
1641 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1642 printf("DrawGraphic(): This should never happen!\n");
1647 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1650 MarkTileDirty(x, y);
1653 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1656 if (!IN_SCR_FIELD(x, y))
1658 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1659 printf("DrawGraphic(): This should never happen!\n");
1664 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1666 MarkTileDirty(x, y);
1669 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1675 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1677 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1680 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1686 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1687 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1690 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1693 if (!IN_SCR_FIELD(x, y))
1695 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1696 printf("DrawGraphicThruMask(): This should never happen!\n");
1701 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1704 MarkTileDirty(x, y);
1707 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1710 if (!IN_SCR_FIELD(x, y))
1712 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1713 printf("DrawGraphicThruMask(): This should never happen!\n");
1718 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1720 MarkTileDirty(x, y);
1723 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1729 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1731 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1735 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1736 int graphic, int frame)
1741 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1743 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1747 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1749 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1751 MarkTileDirty(x / tilesize, y / tilesize);
1754 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1757 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1758 graphic, frame, tilesize);
1759 MarkTileDirty(x / tilesize, y / tilesize);
1762 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1768 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1769 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1772 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1773 int frame, int tilesize)
1778 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1779 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1782 void DrawMiniGraphic(int x, int y, int graphic)
1784 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1785 MarkTileDirty(x / 2, y / 2);
1788 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1793 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1794 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1797 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1798 int graphic, int frame,
1799 int cut_mode, int mask_mode)
1804 int width = TILEX, height = TILEY;
1807 if (dx || dy) // shifted graphic
1809 if (x < BX1) // object enters playfield from the left
1816 else if (x > BX2) // object enters playfield from the right
1822 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1828 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1830 else if (dx) // general horizontal movement
1831 MarkTileDirty(x + SIGN(dx), y);
1833 if (y < BY1) // object enters playfield from the top
1835 if (cut_mode == CUT_BELOW) // object completely above top border
1843 else if (y > BY2) // object enters playfield from the bottom
1849 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1855 else if (dy > 0 && cut_mode == CUT_ABOVE)
1857 if (y == BY2) // object completely above bottom border
1863 MarkTileDirty(x, y + 1);
1864 } // object leaves playfield to the bottom
1865 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1867 else if (dy) // general vertical movement
1868 MarkTileDirty(x, y + SIGN(dy));
1872 if (!IN_SCR_FIELD(x, y))
1874 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1875 printf("DrawGraphicShifted(): This should never happen!\n");
1880 width = width * TILESIZE_VAR / TILESIZE;
1881 height = height * TILESIZE_VAR / TILESIZE;
1882 cx = cx * TILESIZE_VAR / TILESIZE;
1883 cy = cy * TILESIZE_VAR / TILESIZE;
1884 dx = dx * TILESIZE_VAR / TILESIZE;
1885 dy = dy * TILESIZE_VAR / TILESIZE;
1887 if (width > 0 && height > 0)
1889 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1894 dst_x = FX + x * TILEX_VAR + dx;
1895 dst_y = FY + y * TILEY_VAR + dy;
1897 if (mask_mode == USE_MASKING)
1898 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1901 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1904 MarkTileDirty(x, y);
1908 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1909 int graphic, int frame,
1910 int cut_mode, int mask_mode)
1915 int width = TILEX_VAR, height = TILEY_VAR;
1918 int x2 = x + SIGN(dx);
1919 int y2 = y + SIGN(dy);
1921 // movement with two-tile animations must be sync'ed with movement position,
1922 // not with current GfxFrame (which can be higher when using slow movement)
1923 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1924 int anim_frames = graphic_info[graphic].anim_frames;
1926 // (we also need anim_delay here for movement animations with less frames)
1927 int anim_delay = graphic_info[graphic].anim_delay;
1928 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1930 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1931 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1933 // re-calculate animation frame for two-tile movement animation
1934 frame = getGraphicAnimationFrame(graphic, sync_frame);
1936 // check if movement start graphic inside screen area and should be drawn
1937 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1939 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1941 dst_x = FX + x1 * TILEX_VAR;
1942 dst_y = FY + y1 * TILEY_VAR;
1944 if (mask_mode == USE_MASKING)
1945 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1948 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 MarkTileDirty(x1, y1);
1954 // check if movement end graphic inside screen area and should be drawn
1955 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1957 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1959 dst_x = FX + x2 * TILEX_VAR;
1960 dst_y = FY + y2 * TILEY_VAR;
1962 if (mask_mode == USE_MASKING)
1963 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1966 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1969 MarkTileDirty(x2, y2);
1973 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1974 int graphic, int frame,
1975 int cut_mode, int mask_mode)
1979 DrawGraphic(x, y, graphic, frame);
1984 if (graphic_info[graphic].double_movement) // EM style movement images
1985 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1987 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1990 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1991 int graphic, int frame, int cut_mode)
1993 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1996 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1997 int cut_mode, int mask_mode)
1999 int lx = LEVELX(x), ly = LEVELY(y);
2003 if (IN_LEV_FIELD(lx, ly))
2005 SetRandomAnimationValue(lx, ly);
2007 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2008 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2010 // do not use double (EM style) movement graphic when not moving
2011 if (graphic_info[graphic].double_movement && !dx && !dy)
2013 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2014 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2017 else // border element
2019 graphic = el2img(element);
2020 frame = getGraphicAnimationFrame(graphic, -1);
2023 if (element == EL_EXPANDABLE_WALL)
2025 boolean left_stopped = FALSE, right_stopped = FALSE;
2027 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2028 left_stopped = TRUE;
2029 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2030 right_stopped = TRUE;
2032 if (left_stopped && right_stopped)
2034 else if (left_stopped)
2036 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2037 frame = graphic_info[graphic].anim_frames - 1;
2039 else if (right_stopped)
2041 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2042 frame = graphic_info[graphic].anim_frames - 1;
2047 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2048 else if (mask_mode == USE_MASKING)
2049 DrawGraphicThruMask(x, y, graphic, frame);
2051 DrawGraphic(x, y, graphic, frame);
2054 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2055 int cut_mode, int mask_mode)
2057 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2058 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2059 cut_mode, mask_mode);
2062 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2065 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2068 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2071 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2074 void DrawLevelElementThruMask(int x, int y, int element)
2076 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2079 void DrawLevelFieldThruMask(int x, int y)
2081 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2084 // !!! implementation of quicksand is totally broken !!!
2085 #define IS_CRUMBLED_TILE(x, y, e) \
2086 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2087 !IS_MOVING(x, y) || \
2088 (e) == EL_QUICKSAND_EMPTYING || \
2089 (e) == EL_QUICKSAND_FAST_EMPTYING))
2091 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2096 int width, height, cx, cy;
2097 int sx = SCREENX(x), sy = SCREENY(y);
2098 int crumbled_border_size = graphic_info[graphic].border_size;
2099 int crumbled_tile_size = graphic_info[graphic].tile_size;
2100 int crumbled_border_size_var =
2101 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2104 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2106 for (i = 1; i < 4; i++)
2108 int dxx = (i & 1 ? dx : 0);
2109 int dyy = (i & 2 ? dy : 0);
2112 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2115 // check if neighbour field is of same crumble type
2116 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2117 graphic_info[graphic].class ==
2118 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2120 // return if check prevents inner corner
2121 if (same == (dxx == dx && dyy == dy))
2125 // if we reach this point, we have an inner corner
2127 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2129 width = crumbled_border_size_var;
2130 height = crumbled_border_size_var;
2131 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2132 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2134 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2135 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2138 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2143 int width, height, bx, by, cx, cy;
2144 int sx = SCREENX(x), sy = SCREENY(y);
2145 int crumbled_border_size = graphic_info[graphic].border_size;
2146 int crumbled_tile_size = graphic_info[graphic].tile_size;
2147 int crumbled_border_size_var =
2148 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2149 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2152 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2154 // draw simple, sloppy, non-corner-accurate crumbled border
2156 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2157 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2158 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2159 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2161 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2162 FX + sx * TILEX_VAR + cx,
2163 FY + sy * TILEY_VAR + cy);
2165 // (remaining middle border part must be at least as big as corner part)
2166 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2167 crumbled_border_size_var >= TILESIZE_VAR / 3)
2170 // correct corners of crumbled border, if needed
2172 for (i = -1; i <= 1; i += 2)
2174 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2175 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2176 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2179 // check if neighbour field is of same crumble type
2180 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2181 graphic_info[graphic].class ==
2182 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2184 // no crumbled corner, but continued crumbled border
2186 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2187 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2188 int b1 = (i == 1 ? crumbled_border_size_var :
2189 TILESIZE_VAR - 2 * crumbled_border_size_var);
2191 width = crumbled_border_size_var;
2192 height = crumbled_border_size_var;
2194 if (dir == 1 || dir == 2)
2209 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2211 FX + sx * TILEX_VAR + cx,
2212 FY + sy * TILEY_VAR + cy);
2217 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2219 int sx = SCREENX(x), sy = SCREENY(y);
2222 static int xy[4][2] =
2230 if (!IN_LEV_FIELD(x, y))
2233 element = TILE_GFX_ELEMENT(x, y);
2235 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2237 if (!IN_SCR_FIELD(sx, sy))
2240 // crumble field borders towards direct neighbour fields
2241 for (i = 0; i < 4; i++)
2243 int xx = x + xy[i][0];
2244 int yy = y + xy[i][1];
2246 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2249 // check if neighbour field is of same crumble type
2250 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2251 graphic_info[graphic].class ==
2252 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2255 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2258 // crumble inner field corners towards corner neighbour fields
2259 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2260 graphic_info[graphic].anim_frames == 2)
2262 for (i = 0; i < 4; i++)
2264 int dx = (i & 1 ? +1 : -1);
2265 int dy = (i & 2 ? +1 : -1);
2267 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2271 MarkTileDirty(sx, sy);
2273 else // center field is not crumbled -- crumble neighbour fields
2275 // crumble field borders of direct neighbour fields
2276 for (i = 0; i < 4; i++)
2278 int xx = x + xy[i][0];
2279 int yy = y + xy[i][1];
2280 int sxx = sx + xy[i][0];
2281 int syy = sy + xy[i][1];
2283 if (!IN_LEV_FIELD(xx, yy) ||
2284 !IN_SCR_FIELD(sxx, syy))
2287 // do not crumble fields that are being digged or snapped
2288 if (Feld[xx][yy] == EL_EMPTY ||
2289 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2292 element = TILE_GFX_ELEMENT(xx, yy);
2294 if (!IS_CRUMBLED_TILE(xx, yy, element))
2297 graphic = el_act2crm(element, ACTION_DEFAULT);
2299 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2301 MarkTileDirty(sxx, syy);
2304 // crumble inner field corners of corner neighbour fields
2305 for (i = 0; i < 4; i++)
2307 int dx = (i & 1 ? +1 : -1);
2308 int dy = (i & 2 ? +1 : -1);
2314 if (!IN_LEV_FIELD(xx, yy) ||
2315 !IN_SCR_FIELD(sxx, syy))
2318 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2321 element = TILE_GFX_ELEMENT(xx, yy);
2323 if (!IS_CRUMBLED_TILE(xx, yy, element))
2326 graphic = el_act2crm(element, ACTION_DEFAULT);
2328 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2329 graphic_info[graphic].anim_frames == 2)
2330 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2332 MarkTileDirty(sxx, syy);
2337 void DrawLevelFieldCrumbled(int x, int y)
2341 if (!IN_LEV_FIELD(x, y))
2344 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2345 GfxElement[x][y] != EL_UNDEFINED &&
2346 GFX_CRUMBLED(GfxElement[x][y]))
2348 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2353 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2355 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2358 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2361 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2362 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2363 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2364 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2365 int sx = SCREENX(x), sy = SCREENY(y);
2367 DrawGraphic(sx, sy, graphic1, frame1);
2368 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2371 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2373 int sx = SCREENX(x), sy = SCREENY(y);
2374 static int xy[4][2] =
2383 // crumble direct neighbour fields (required for field borders)
2384 for (i = 0; i < 4; i++)
2386 int xx = x + xy[i][0];
2387 int yy = y + xy[i][1];
2388 int sxx = sx + xy[i][0];
2389 int syy = sy + xy[i][1];
2391 if (!IN_LEV_FIELD(xx, yy) ||
2392 !IN_SCR_FIELD(sxx, syy) ||
2393 !GFX_CRUMBLED(Feld[xx][yy]) ||
2397 DrawLevelField(xx, yy);
2400 // crumble corner neighbour fields (required for inner field corners)
2401 for (i = 0; i < 4; i++)
2403 int dx = (i & 1 ? +1 : -1);
2404 int dy = (i & 2 ? +1 : -1);
2410 if (!IN_LEV_FIELD(xx, yy) ||
2411 !IN_SCR_FIELD(sxx, syy) ||
2412 !GFX_CRUMBLED(Feld[xx][yy]) ||
2416 int element = TILE_GFX_ELEMENT(xx, yy);
2417 int graphic = el_act2crm(element, ACTION_DEFAULT);
2419 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2420 graphic_info[graphic].anim_frames == 2)
2421 DrawLevelField(xx, yy);
2425 static int getBorderElement(int x, int y)
2429 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2430 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2431 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2432 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2433 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2434 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2435 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2437 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2438 int steel_position = (x == -1 && y == -1 ? 0 :
2439 x == lev_fieldx && y == -1 ? 1 :
2440 x == -1 && y == lev_fieldy ? 2 :
2441 x == lev_fieldx && y == lev_fieldy ? 3 :
2442 x == -1 || x == lev_fieldx ? 4 :
2443 y == -1 || y == lev_fieldy ? 5 : 6);
2445 return border[steel_position][steel_type];
2448 void DrawScreenElement(int x, int y, int element)
2450 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2451 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2454 void DrawLevelElement(int x, int y, int element)
2456 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2457 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2460 void DrawScreenField(int x, int y)
2462 int lx = LEVELX(x), ly = LEVELY(y);
2463 int element, content;
2465 if (!IN_LEV_FIELD(lx, ly))
2467 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2470 element = getBorderElement(lx, ly);
2472 DrawScreenElement(x, y, element);
2477 element = Feld[lx][ly];
2478 content = Store[lx][ly];
2480 if (IS_MOVING(lx, ly))
2482 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2483 boolean cut_mode = NO_CUTTING;
2485 if (element == EL_QUICKSAND_EMPTYING ||
2486 element == EL_QUICKSAND_FAST_EMPTYING ||
2487 element == EL_MAGIC_WALL_EMPTYING ||
2488 element == EL_BD_MAGIC_WALL_EMPTYING ||
2489 element == EL_DC_MAGIC_WALL_EMPTYING ||
2490 element == EL_AMOEBA_DROPPING)
2491 cut_mode = CUT_ABOVE;
2492 else if (element == EL_QUICKSAND_FILLING ||
2493 element == EL_QUICKSAND_FAST_FILLING ||
2494 element == EL_MAGIC_WALL_FILLING ||
2495 element == EL_BD_MAGIC_WALL_FILLING ||
2496 element == EL_DC_MAGIC_WALL_FILLING)
2497 cut_mode = CUT_BELOW;
2499 if (cut_mode == CUT_ABOVE)
2500 DrawScreenElement(x, y, element);
2502 DrawScreenElement(x, y, EL_EMPTY);
2505 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2506 else if (cut_mode == NO_CUTTING)
2507 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2510 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2512 if (cut_mode == CUT_BELOW &&
2513 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2514 DrawLevelElement(lx, ly + 1, element);
2517 if (content == EL_ACID)
2519 int dir = MovDir[lx][ly];
2520 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2521 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2523 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2525 // prevent target field from being drawn again (but without masking)
2526 // (this would happen if target field is scanned after moving element)
2527 Stop[newlx][newly] = TRUE;
2530 else if (IS_BLOCKED(lx, ly))
2535 boolean cut_mode = NO_CUTTING;
2536 int element_old, content_old;
2538 Blocked2Moving(lx, ly, &oldx, &oldy);
2541 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2542 MovDir[oldx][oldy] == MV_RIGHT);
2544 element_old = Feld[oldx][oldy];
2545 content_old = Store[oldx][oldy];
2547 if (element_old == EL_QUICKSAND_EMPTYING ||
2548 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2549 element_old == EL_MAGIC_WALL_EMPTYING ||
2550 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2551 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2552 element_old == EL_AMOEBA_DROPPING)
2553 cut_mode = CUT_ABOVE;
2555 DrawScreenElement(x, y, EL_EMPTY);
2558 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2560 else if (cut_mode == NO_CUTTING)
2561 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2564 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2567 else if (IS_DRAWABLE(element))
2568 DrawScreenElement(x, y, element);
2570 DrawScreenElement(x, y, EL_EMPTY);
2573 void DrawLevelField(int x, int y)
2575 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2576 DrawScreenField(SCREENX(x), SCREENY(y));
2577 else if (IS_MOVING(x, y))
2581 Moving2Blocked(x, y, &newx, &newy);
2582 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2583 DrawScreenField(SCREENX(newx), SCREENY(newy));
2585 else if (IS_BLOCKED(x, y))
2589 Blocked2Moving(x, y, &oldx, &oldy);
2590 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2591 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2595 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2596 int (*el2img_function)(int), boolean masked,
2597 int element_bits_draw)
2599 int element_base = map_mm_wall_element(element);
2600 int element_bits = (IS_DF_WALL(element) ?
2601 element - EL_DF_WALL_START :
2602 IS_MM_WALL(element) ?
2603 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2604 int graphic = el2img_function(element_base);
2605 int tilesize_draw = tilesize / 2;
2610 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2612 for (i = 0; i < 4; i++)
2614 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2615 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2617 if (!(element_bits_draw & (1 << i)))
2620 if (element_bits & (1 << i))
2623 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2624 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2626 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2627 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2632 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2633 tilesize_draw, tilesize_draw);
2638 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2639 boolean masked, int element_bits_draw)
2641 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2642 element, tilesize, el2edimg, masked, element_bits_draw);
2645 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2646 int (*el2img_function)(int))
2648 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2652 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2655 if (IS_MM_WALL(element))
2657 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2658 element, tilesize, el2edimg, masked, 0x000f);
2662 int graphic = el2edimg(element);
2665 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2667 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2671 void DrawSizedElement(int x, int y, int element, int tilesize)
2673 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2676 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2678 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2681 void DrawMiniElement(int x, int y, int element)
2685 graphic = el2edimg(element);
2686 DrawMiniGraphic(x, y, graphic);
2689 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2692 int x = sx + scroll_x, y = sy + scroll_y;
2694 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2695 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2696 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2697 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2699 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2702 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2704 int x = sx + scroll_x, y = sy + scroll_y;
2706 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2707 DrawMiniElement(sx, sy, EL_EMPTY);
2708 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2709 DrawMiniElement(sx, sy, Feld[x][y]);
2711 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2714 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2715 int x, int y, int xsize, int ysize,
2716 int tile_width, int tile_height)
2720 int dst_x = startx + x * tile_width;
2721 int dst_y = starty + y * tile_height;
2722 int width = graphic_info[graphic].width;
2723 int height = graphic_info[graphic].height;
2724 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2725 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2726 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2727 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2728 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2729 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2730 boolean draw_masked = graphic_info[graphic].draw_masked;
2732 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2734 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2736 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2740 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2741 inner_sx + (x - 1) * tile_width % inner_width);
2742 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2743 inner_sy + (y - 1) * tile_height % inner_height);
2746 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2749 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2753 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2754 int x, int y, int xsize, int ysize,
2757 int font_width = getFontWidth(font_nr);
2758 int font_height = getFontHeight(font_nr);
2760 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2761 font_width, font_height);
2764 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2766 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2767 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2768 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2769 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2770 boolean no_delay = (tape.warp_forward);
2771 unsigned int anim_delay = 0;
2772 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2773 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2774 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2775 int font_width = getFontWidth(font_nr);
2776 int font_height = getFontHeight(font_nr);
2777 int max_xsize = level.envelope[envelope_nr].xsize;
2778 int max_ysize = level.envelope[envelope_nr].ysize;
2779 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2780 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2781 int xend = max_xsize;
2782 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2783 int xstep = (xstart < xend ? 1 : 0);
2784 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2786 int end = MAX(xend - xstart, yend - ystart);
2789 for (i = start; i <= end; i++)
2791 int last_frame = end; // last frame of this "for" loop
2792 int x = xstart + i * xstep;
2793 int y = ystart + i * ystep;
2794 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2795 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2796 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2797 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2800 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2802 BlitScreenToBitmap(backbuffer);
2804 SetDrawtoField(DRAW_TO_BACKBUFFER);
2806 for (yy = 0; yy < ysize; yy++)
2807 for (xx = 0; xx < xsize; xx++)
2808 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2810 DrawTextBuffer(sx + font_width, sy + font_height,
2811 level.envelope[envelope_nr].text, font_nr, max_xsize,
2812 xsize - 2, ysize - 2, 0, mask_mode,
2813 level.envelope[envelope_nr].autowrap,
2814 level.envelope[envelope_nr].centered, FALSE);
2816 redraw_mask |= REDRAW_FIELD;
2819 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2822 ClearAutoRepeatKeyEvents();
2825 void ShowEnvelope(int envelope_nr)
2827 int element = EL_ENVELOPE_1 + envelope_nr;
2828 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2829 int sound_opening = element_info[element].sound[ACTION_OPENING];
2830 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2831 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2832 boolean no_delay = (tape.warp_forward);
2833 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2834 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2835 int anim_mode = graphic_info[graphic].anim_mode;
2836 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2837 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2839 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2841 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2843 if (anim_mode == ANIM_DEFAULT)
2844 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2846 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2849 Delay_WithScreenUpdates(wait_delay_value);
2851 WaitForEventToContinue();
2853 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2855 if (anim_mode != ANIM_NONE)
2856 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2858 if (anim_mode == ANIM_DEFAULT)
2859 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2861 game.envelope_active = FALSE;
2863 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2865 redraw_mask |= REDRAW_FIELD;
2869 static void setRequestBasePosition(int *x, int *y)
2871 int sx_base, sy_base;
2873 if (request.x != -1)
2874 sx_base = request.x;
2875 else if (request.align == ALIGN_LEFT)
2877 else if (request.align == ALIGN_RIGHT)
2878 sx_base = SX + SXSIZE;
2880 sx_base = SX + SXSIZE / 2;
2882 if (request.y != -1)
2883 sy_base = request.y;
2884 else if (request.valign == VALIGN_TOP)
2886 else if (request.valign == VALIGN_BOTTOM)
2887 sy_base = SY + SYSIZE;
2889 sy_base = SY + SYSIZE / 2;
2895 static void setRequestPositionExt(int *x, int *y, int width, int height,
2896 boolean add_border_size)
2898 int border_size = request.border_size;
2899 int sx_base, sy_base;
2902 setRequestBasePosition(&sx_base, &sy_base);
2904 if (request.align == ALIGN_LEFT)
2906 else if (request.align == ALIGN_RIGHT)
2907 sx = sx_base - width;
2909 sx = sx_base - width / 2;
2911 if (request.valign == VALIGN_TOP)
2913 else if (request.valign == VALIGN_BOTTOM)
2914 sy = sy_base - height;
2916 sy = sy_base - height / 2;
2918 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2919 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2921 if (add_border_size)
2931 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2933 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2936 static void DrawEnvelopeRequest(char *text)
2938 char *text_final = text;
2939 char *text_door_style = NULL;
2940 int graphic = IMG_BACKGROUND_REQUEST;
2941 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2942 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2943 int font_nr = FONT_REQUEST;
2944 int font_width = getFontWidth(font_nr);
2945 int font_height = getFontHeight(font_nr);
2946 int border_size = request.border_size;
2947 int line_spacing = request.line_spacing;
2948 int line_height = font_height + line_spacing;
2949 int max_text_width = request.width - 2 * border_size;
2950 int max_text_height = request.height - 2 * border_size;
2951 int line_length = max_text_width / font_width;
2952 int max_lines = max_text_height / line_height;
2953 int text_width = line_length * font_width;
2954 int width = request.width;
2955 int height = request.height;
2956 int tile_size = MAX(request.step_offset, 1);
2957 int x_steps = width / tile_size;
2958 int y_steps = height / tile_size;
2959 int sx_offset = border_size;
2960 int sy_offset = border_size;
2964 if (request.centered)
2965 sx_offset = (request.width - text_width) / 2;
2967 if (request.wrap_single_words && !request.autowrap)
2969 char *src_text_ptr, *dst_text_ptr;
2971 text_door_style = checked_malloc(2 * strlen(text) + 1);
2973 src_text_ptr = text;
2974 dst_text_ptr = text_door_style;
2976 while (*src_text_ptr)
2978 if (*src_text_ptr == ' ' ||
2979 *src_text_ptr == '?' ||
2980 *src_text_ptr == '!')
2981 *dst_text_ptr++ = '\n';
2983 if (*src_text_ptr != ' ')
2984 *dst_text_ptr++ = *src_text_ptr;
2989 *dst_text_ptr = '\0';
2991 text_final = text_door_style;
2994 setRequestPosition(&sx, &sy, FALSE);
2996 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2998 for (y = 0; y < y_steps; y++)
2999 for (x = 0; x < x_steps; x++)
3000 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3001 x, y, x_steps, y_steps,
3002 tile_size, tile_size);
3004 // force DOOR font inside door area
3005 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3007 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3008 line_length, -1, max_lines, line_spacing, mask_mode,
3009 request.autowrap, request.centered, FALSE);
3013 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3014 RedrawGadget(tool_gadget[i]);
3016 // store readily prepared envelope request for later use when animating
3017 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3019 if (text_door_style)
3020 free(text_door_style);
3023 static void AnimateEnvelopeRequest(int anim_mode, int action)
3025 int graphic = IMG_BACKGROUND_REQUEST;
3026 boolean draw_masked = graphic_info[graphic].draw_masked;
3027 int delay_value_normal = request.step_delay;
3028 int delay_value_fast = delay_value_normal / 2;
3029 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3030 boolean no_delay = (tape.warp_forward);
3031 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3032 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3033 unsigned int anim_delay = 0;
3035 int tile_size = MAX(request.step_offset, 1);
3036 int max_xsize = request.width / tile_size;
3037 int max_ysize = request.height / tile_size;
3038 int max_xsize_inner = max_xsize - 2;
3039 int max_ysize_inner = max_ysize - 2;
3041 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3042 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3043 int xend = max_xsize_inner;
3044 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3045 int xstep = (xstart < xend ? 1 : 0);
3046 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3048 int end = MAX(xend - xstart, yend - ystart);
3051 if (setup.quick_doors)
3058 for (i = start; i <= end; i++)
3060 int last_frame = end; // last frame of this "for" loop
3061 int x = xstart + i * xstep;
3062 int y = ystart + i * ystep;
3063 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3064 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3065 int xsize_size_left = (xsize - 1) * tile_size;
3066 int ysize_size_top = (ysize - 1) * tile_size;
3067 int max_xsize_pos = (max_xsize - 1) * tile_size;
3068 int max_ysize_pos = (max_ysize - 1) * tile_size;
3069 int width = xsize * tile_size;
3070 int height = ysize * tile_size;
3075 setRequestPosition(&src_x, &src_y, FALSE);
3076 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3078 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3080 for (yy = 0; yy < 2; yy++)
3082 for (xx = 0; xx < 2; xx++)
3084 int src_xx = src_x + xx * max_xsize_pos;
3085 int src_yy = src_y + yy * max_ysize_pos;
3086 int dst_xx = dst_x + xx * xsize_size_left;
3087 int dst_yy = dst_y + yy * ysize_size_top;
3088 int xx_size = (xx ? tile_size : xsize_size_left);
3089 int yy_size = (yy ? tile_size : ysize_size_top);
3092 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3093 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3095 BlitBitmap(bitmap_db_store_2, backbuffer,
3096 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3100 redraw_mask |= REDRAW_FIELD;
3104 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3107 ClearAutoRepeatKeyEvents();
3110 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3112 int graphic = IMG_BACKGROUND_REQUEST;
3113 int sound_opening = SND_REQUEST_OPENING;
3114 int sound_closing = SND_REQUEST_CLOSING;
3115 int anim_mode_1 = request.anim_mode; // (higher priority)
3116 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3117 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3118 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3119 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3121 if (game_status == GAME_MODE_PLAYING)
3122 BlitScreenToBitmap(backbuffer);
3124 SetDrawtoField(DRAW_TO_BACKBUFFER);
3126 // SetDrawBackgroundMask(REDRAW_NONE);
3128 if (action == ACTION_OPENING)
3130 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3132 if (req_state & REQ_ASK)
3134 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3135 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3137 else if (req_state & REQ_CONFIRM)
3139 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3141 else if (req_state & REQ_PLAYER)
3143 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3144 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3145 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3146 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3149 DrawEnvelopeRequest(text);
3152 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3154 if (action == ACTION_OPENING)
3156 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3158 if (anim_mode == ANIM_DEFAULT)
3159 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3161 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3165 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3167 if (anim_mode != ANIM_NONE)
3168 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3170 if (anim_mode == ANIM_DEFAULT)
3171 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3174 game.envelope_active = FALSE;
3176 if (action == ACTION_CLOSING)
3177 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3179 // SetDrawBackgroundMask(last_draw_background_mask);
3181 redraw_mask |= REDRAW_FIELD;
3185 if (action == ACTION_CLOSING &&
3186 game_status == GAME_MODE_PLAYING &&
3187 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3188 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3191 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3193 if (IS_MM_WALL(element))
3195 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3201 int graphic = el2preimg(element);
3203 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3204 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3209 void DrawLevel(int draw_background_mask)
3213 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3214 SetDrawBackgroundMask(draw_background_mask);
3218 for (x = BX1; x <= BX2; x++)
3219 for (y = BY1; y <= BY2; y++)
3220 DrawScreenField(x, y);
3222 redraw_mask |= REDRAW_FIELD;
3225 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3230 for (x = 0; x < size_x; x++)
3231 for (y = 0; y < size_y; y++)
3232 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3234 redraw_mask |= REDRAW_FIELD;
3237 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3241 for (x = 0; x < size_x; x++)
3242 for (y = 0; y < size_y; y++)
3243 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3245 redraw_mask |= REDRAW_FIELD;
3248 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3250 boolean show_level_border = (BorderElement != EL_EMPTY);
3251 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3252 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3253 int tile_size = preview.tile_size;
3254 int preview_width = preview.xsize * tile_size;
3255 int preview_height = preview.ysize * tile_size;
3256 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3257 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3258 int real_preview_width = real_preview_xsize * tile_size;
3259 int real_preview_height = real_preview_ysize * tile_size;
3260 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3261 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3264 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3267 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3269 dst_x += (preview_width - real_preview_width) / 2;
3270 dst_y += (preview_height - real_preview_height) / 2;
3272 for (x = 0; x < real_preview_xsize; x++)
3274 for (y = 0; y < real_preview_ysize; y++)
3276 int lx = from_x + x + (show_level_border ? -1 : 0);
3277 int ly = from_y + y + (show_level_border ? -1 : 0);
3278 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3279 getBorderElement(lx, ly));
3281 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3282 element, tile_size);
3286 redraw_mask |= REDRAW_FIELD;
3289 #define MICROLABEL_EMPTY 0
3290 #define MICROLABEL_LEVEL_NAME 1
3291 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3292 #define MICROLABEL_LEVEL_AUTHOR 3
3293 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3294 #define MICROLABEL_IMPORTED_FROM 5
3295 #define MICROLABEL_IMPORTED_BY_HEAD 6
3296 #define MICROLABEL_IMPORTED_BY 7
3298 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3300 int max_text_width = SXSIZE;
3301 int font_width = getFontWidth(font_nr);
3303 if (pos->align == ALIGN_CENTER)
3304 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3305 else if (pos->align == ALIGN_RIGHT)
3306 max_text_width = pos->x;
3308 max_text_width = SXSIZE - pos->x;
3310 return max_text_width / font_width;
3313 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3315 char label_text[MAX_OUTPUT_LINESIZE + 1];
3316 int max_len_label_text;
3317 int font_nr = pos->font;
3320 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3323 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3324 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3325 mode == MICROLABEL_IMPORTED_BY_HEAD)
3326 font_nr = pos->font_alt;
3328 max_len_label_text = getMaxTextLength(pos, font_nr);
3330 if (pos->size != -1)
3331 max_len_label_text = pos->size;
3333 for (i = 0; i < max_len_label_text; i++)
3334 label_text[i] = ' ';
3335 label_text[max_len_label_text] = '\0';
3337 if (strlen(label_text) > 0)
3338 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3341 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3342 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3343 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3344 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3345 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3346 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3347 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3348 max_len_label_text);
3349 label_text[max_len_label_text] = '\0';
3351 if (strlen(label_text) > 0)
3352 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3354 redraw_mask |= REDRAW_FIELD;
3357 static void DrawPreviewLevelLabel(int mode)
3359 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3362 static void DrawPreviewLevelInfo(int mode)
3364 if (mode == MICROLABEL_LEVEL_NAME)
3365 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3366 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3367 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3370 static void DrawPreviewLevelExt(boolean restart)
3372 static unsigned int scroll_delay = 0;
3373 static unsigned int label_delay = 0;
3374 static int from_x, from_y, scroll_direction;
3375 static int label_state, label_counter;
3376 unsigned int scroll_delay_value = preview.step_delay;
3377 boolean show_level_border = (BorderElement != EL_EMPTY);
3378 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3379 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3386 if (preview.anim_mode == ANIM_CENTERED)
3388 if (level_xsize > preview.xsize)
3389 from_x = (level_xsize - preview.xsize) / 2;
3390 if (level_ysize > preview.ysize)
3391 from_y = (level_ysize - preview.ysize) / 2;
3394 from_x += preview.xoffset;
3395 from_y += preview.yoffset;
3397 scroll_direction = MV_RIGHT;
3401 DrawPreviewLevelPlayfield(from_x, from_y);
3402 DrawPreviewLevelLabel(label_state);
3404 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3405 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3407 // initialize delay counters
3408 DelayReached(&scroll_delay, 0);
3409 DelayReached(&label_delay, 0);
3411 if (leveldir_current->name)
3413 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3414 char label_text[MAX_OUTPUT_LINESIZE + 1];
3415 int font_nr = pos->font;
3416 int max_len_label_text = getMaxTextLength(pos, font_nr);
3418 if (pos->size != -1)
3419 max_len_label_text = pos->size;
3421 strncpy(label_text, leveldir_current->name, max_len_label_text);
3422 label_text[max_len_label_text] = '\0';
3424 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3425 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3431 // scroll preview level, if needed
3432 if (preview.anim_mode != ANIM_NONE &&
3433 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3434 DelayReached(&scroll_delay, scroll_delay_value))
3436 switch (scroll_direction)
3441 from_x -= preview.step_offset;
3442 from_x = (from_x < 0 ? 0 : from_x);
3445 scroll_direction = MV_UP;
3449 if (from_x < level_xsize - preview.xsize)
3451 from_x += preview.step_offset;
3452 from_x = (from_x > level_xsize - preview.xsize ?
3453 level_xsize - preview.xsize : from_x);
3456 scroll_direction = MV_DOWN;
3462 from_y -= preview.step_offset;
3463 from_y = (from_y < 0 ? 0 : from_y);
3466 scroll_direction = MV_RIGHT;
3470 if (from_y < level_ysize - preview.ysize)
3472 from_y += preview.step_offset;
3473 from_y = (from_y > level_ysize - preview.ysize ?
3474 level_ysize - preview.ysize : from_y);
3477 scroll_direction = MV_LEFT;
3484 DrawPreviewLevelPlayfield(from_x, from_y);
3487 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3488 // redraw micro level label, if needed
3489 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3490 !strEqual(level.author, ANONYMOUS_NAME) &&
3491 !strEqual(level.author, leveldir_current->name) &&
3492 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3494 int max_label_counter = 23;
3496 if (leveldir_current->imported_from != NULL &&
3497 strlen(leveldir_current->imported_from) > 0)
3498 max_label_counter += 14;
3499 if (leveldir_current->imported_by != NULL &&
3500 strlen(leveldir_current->imported_by) > 0)
3501 max_label_counter += 14;
3503 label_counter = (label_counter + 1) % max_label_counter;
3504 label_state = (label_counter >= 0 && label_counter <= 7 ?
3505 MICROLABEL_LEVEL_NAME :
3506 label_counter >= 9 && label_counter <= 12 ?
3507 MICROLABEL_LEVEL_AUTHOR_HEAD :
3508 label_counter >= 14 && label_counter <= 21 ?
3509 MICROLABEL_LEVEL_AUTHOR :
3510 label_counter >= 23 && label_counter <= 26 ?
3511 MICROLABEL_IMPORTED_FROM_HEAD :
3512 label_counter >= 28 && label_counter <= 35 ?
3513 MICROLABEL_IMPORTED_FROM :
3514 label_counter >= 37 && label_counter <= 40 ?
3515 MICROLABEL_IMPORTED_BY_HEAD :
3516 label_counter >= 42 && label_counter <= 49 ?
3517 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3519 if (leveldir_current->imported_from == NULL &&
3520 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3521 label_state == MICROLABEL_IMPORTED_FROM))
3522 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3523 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3525 DrawPreviewLevelLabel(label_state);
3529 void DrawPreviewPlayers(void)
3531 if (game_status != GAME_MODE_MAIN)
3534 // do not draw preview players if level preview redefined, but players aren't
3535 if (preview.redefined && !menu.main.preview_players.redefined)
3538 boolean player_found[MAX_PLAYERS];
3539 int num_players = 0;
3542 for (i = 0; i < MAX_PLAYERS; i++)
3543 player_found[i] = FALSE;
3545 // check which players can be found in the level (simple approach)
3546 for (x = 0; x < lev_fieldx; x++)
3548 for (y = 0; y < lev_fieldy; y++)
3550 int element = level.field[x][y];
3552 if (ELEM_IS_PLAYER(element))
3554 int player_nr = GET_PLAYER_NR(element);
3556 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3558 if (!player_found[player_nr])
3561 player_found[player_nr] = TRUE;
3566 struct TextPosInfo *pos = &menu.main.preview_players;
3567 int tile_size = pos->tile_size;
3568 int border_size = pos->border_size;
3569 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3570 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3571 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3572 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3573 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3574 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3575 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3576 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3577 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3578 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3579 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3580 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3582 // clear area in which the players will be drawn
3583 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3584 max_players_width, max_players_height);
3586 if (!network.enabled && !setup.team_mode)
3589 // only draw players if level is suited for team mode
3590 if (num_players < 2)
3593 // draw all players that were found in the level
3594 for (i = 0; i < MAX_PLAYERS; i++)
3596 if (player_found[i])
3598 int graphic = el2img(EL_PLAYER_1 + i);
3600 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3602 xpos += player_xoffset;
3603 ypos += player_yoffset;
3608 void DrawPreviewLevelInitial(void)
3610 DrawPreviewLevelExt(TRUE);
3611 DrawPreviewPlayers();
3614 void DrawPreviewLevelAnimation(void)
3616 DrawPreviewLevelExt(FALSE);
3619 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3620 int border_size, int font_nr)
3622 int graphic = el2img(EL_PLAYER_1 + player_nr);
3623 int font_height = getFontHeight(font_nr);
3624 int player_height = MAX(tile_size, font_height);
3625 int xoffset_text = tile_size + border_size;
3626 int yoffset_text = (player_height - font_height) / 2;
3627 int yoffset_graphic = (player_height - tile_size) / 2;
3628 char *player_name = getNetworkPlayerName(player_nr + 1);
3630 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3632 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3635 static void DrawNetworkPlayersExt(boolean force)
3637 if (game_status != GAME_MODE_MAIN)
3640 if (!network.connected && !force)
3643 // do not draw network players if level preview redefined, but players aren't
3644 if (preview.redefined && !menu.main.network_players.redefined)
3647 int num_players = 0;
3650 for (i = 0; i < MAX_PLAYERS; i++)
3651 if (stored_player[i].connected_network)
3654 struct TextPosInfo *pos = &menu.main.network_players;
3655 int tile_size = pos->tile_size;
3656 int border_size = pos->border_size;
3657 int xoffset_text = tile_size + border_size;
3658 int font_nr = pos->font;
3659 int font_width = getFontWidth(font_nr);
3660 int font_height = getFontHeight(font_nr);
3661 int player_height = MAX(tile_size, font_height);
3662 int player_yoffset = player_height + border_size;
3663 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3664 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3665 int all_players_height = num_players * player_yoffset - border_size;
3666 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3667 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3668 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3670 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3671 max_players_width, max_players_height);
3673 // first draw local network player ...
3674 for (i = 0; i < MAX_PLAYERS; i++)
3676 if (stored_player[i].connected_network &&
3677 stored_player[i].connected_locally)
3679 char *player_name = getNetworkPlayerName(i + 1);
3680 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3681 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3683 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3685 ypos += player_yoffset;
3689 // ... then draw all other network players
3690 for (i = 0; i < MAX_PLAYERS; i++)
3692 if (stored_player[i].connected_network &&
3693 !stored_player[i].connected_locally)
3695 char *player_name = getNetworkPlayerName(i + 1);
3696 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3697 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3699 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3701 ypos += player_yoffset;
3706 void DrawNetworkPlayers(void)
3708 DrawNetworkPlayersExt(FALSE);
3711 void ClearNetworkPlayers(void)
3713 DrawNetworkPlayersExt(TRUE);
3716 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3717 int graphic, int sync_frame,
3720 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3722 if (mask_mode == USE_MASKING)
3723 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3725 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3728 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3729 int graphic, int sync_frame, int mask_mode)
3731 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3733 if (mask_mode == USE_MASKING)
3734 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3736 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3739 static void DrawGraphicAnimation(int x, int y, int graphic)
3741 int lx = LEVELX(x), ly = LEVELY(y);
3743 if (!IN_SCR_FIELD(x, y))
3746 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3747 graphic, GfxFrame[lx][ly], NO_MASKING);
3749 MarkTileDirty(x, y);
3752 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3754 int lx = LEVELX(x), ly = LEVELY(y);
3756 if (!IN_SCR_FIELD(x, y))
3759 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3760 graphic, GfxFrame[lx][ly], NO_MASKING);
3761 MarkTileDirty(x, y);
3764 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3766 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3769 void DrawLevelElementAnimation(int x, int y, int element)
3771 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3773 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3776 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3778 int sx = SCREENX(x), sy = SCREENY(y);
3780 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3783 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3786 DrawGraphicAnimation(sx, sy, graphic);
3789 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3790 DrawLevelFieldCrumbled(x, y);
3792 if (GFX_CRUMBLED(Feld[x][y]))
3793 DrawLevelFieldCrumbled(x, y);
3797 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3799 int sx = SCREENX(x), sy = SCREENY(y);
3802 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3805 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3807 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3810 DrawGraphicAnimation(sx, sy, graphic);
3812 if (GFX_CRUMBLED(element))
3813 DrawLevelFieldCrumbled(x, y);
3816 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3818 if (player->use_murphy)
3820 // this works only because currently only one player can be "murphy" ...
3821 static int last_horizontal_dir = MV_LEFT;
3822 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3824 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3825 last_horizontal_dir = move_dir;
3827 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3829 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3831 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3837 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3840 static boolean equalGraphics(int graphic1, int graphic2)
3842 struct GraphicInfo *g1 = &graphic_info[graphic1];
3843 struct GraphicInfo *g2 = &graphic_info[graphic2];
3845 return (g1->bitmap == g2->bitmap &&
3846 g1->src_x == g2->src_x &&
3847 g1->src_y == g2->src_y &&
3848 g1->anim_frames == g2->anim_frames &&
3849 g1->anim_delay == g2->anim_delay &&
3850 g1->anim_mode == g2->anim_mode);
3853 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3857 DRAW_PLAYER_STAGE_INIT = 0,
3858 DRAW_PLAYER_STAGE_LAST_FIELD,
3859 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3860 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3861 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3862 DRAW_PLAYER_STAGE_PLAYER,
3864 DRAW_PLAYER_STAGE_PLAYER,
3865 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3867 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3868 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3870 NUM_DRAW_PLAYER_STAGES
3873 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3875 static int static_last_player_graphic[MAX_PLAYERS];
3876 static int static_last_player_frame[MAX_PLAYERS];
3877 static boolean static_player_is_opaque[MAX_PLAYERS];
3878 static boolean draw_player[MAX_PLAYERS];
3879 int pnr = player->index_nr;
3881 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3883 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3884 static_last_player_frame[pnr] = player->Frame;
3885 static_player_is_opaque[pnr] = FALSE;
3887 draw_player[pnr] = TRUE;
3890 if (!draw_player[pnr])
3894 if (!IN_LEV_FIELD(player->jx, player->jy))
3896 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3897 printf("DrawPlayerField(): This should never happen!\n");
3899 draw_player[pnr] = FALSE;
3905 int last_player_graphic = static_last_player_graphic[pnr];
3906 int last_player_frame = static_last_player_frame[pnr];
3907 boolean player_is_opaque = static_player_is_opaque[pnr];
3909 int jx = player->jx;
3910 int jy = player->jy;
3911 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3912 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3913 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3914 int last_jx = (player->is_moving ? jx - dx : jx);
3915 int last_jy = (player->is_moving ? jy - dy : jy);
3916 int next_jx = jx + dx;
3917 int next_jy = jy + dy;
3918 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3919 int sx = SCREENX(jx);
3920 int sy = SCREENY(jy);
3921 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3922 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3923 int element = Feld[jx][jy];
3924 int last_element = Feld[last_jx][last_jy];
3925 int action = (player->is_pushing ? ACTION_PUSHING :
3926 player->is_digging ? ACTION_DIGGING :
3927 player->is_collecting ? ACTION_COLLECTING :
3928 player->is_moving ? ACTION_MOVING :
3929 player->is_snapping ? ACTION_SNAPPING :
3930 player->is_dropping ? ACTION_DROPPING :
3931 player->is_waiting ? player->action_waiting :
3934 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3936 // ------------------------------------------------------------------------
3937 // initialize drawing the player
3938 // ------------------------------------------------------------------------
3940 draw_player[pnr] = FALSE;
3942 // GfxElement[][] is set to the element the player is digging or collecting;
3943 // remove also for off-screen player if the player is not moving anymore
3944 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3945 GfxElement[jx][jy] = EL_UNDEFINED;
3947 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3950 if (element == EL_EXPLOSION)
3953 InitPlayerGfxAnimation(player, action, move_dir);
3955 draw_player[pnr] = TRUE;
3957 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3959 // ------------------------------------------------------------------------
3960 // draw things in the field the player is leaving, if needed
3961 // ------------------------------------------------------------------------
3963 if (!IN_SCR_FIELD(sx, sy))
3964 draw_player[pnr] = FALSE;
3966 if (!player->is_moving)
3969 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3971 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3973 if (last_element == EL_DYNAMITE_ACTIVE ||
3974 last_element == EL_EM_DYNAMITE_ACTIVE ||
3975 last_element == EL_SP_DISK_RED_ACTIVE)
3976 DrawDynamite(last_jx, last_jy);
3978 DrawLevelFieldThruMask(last_jx, last_jy);
3980 else if (last_element == EL_DYNAMITE_ACTIVE ||
3981 last_element == EL_EM_DYNAMITE_ACTIVE ||
3982 last_element == EL_SP_DISK_RED_ACTIVE)
3983 DrawDynamite(last_jx, last_jy);
3985 DrawLevelField(last_jx, last_jy);
3987 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3988 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3990 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3992 // ------------------------------------------------------------------------
3993 // draw things behind the player, if needed
3994 // ------------------------------------------------------------------------
3998 DrawLevelElement(jx, jy, Back[jx][jy]);
4003 if (IS_ACTIVE_BOMB(element))
4005 DrawLevelElement(jx, jy, EL_EMPTY);
4010 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4012 int old_element = GfxElement[jx][jy];
4013 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4014 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4016 if (GFX_CRUMBLED(old_element))
4017 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4019 DrawGraphic(sx, sy, old_graphic, frame);
4021 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4022 static_player_is_opaque[pnr] = TRUE;
4026 GfxElement[jx][jy] = EL_UNDEFINED;
4028 // make sure that pushed elements are drawn with correct frame rate
4029 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4031 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4032 GfxFrame[jx][jy] = player->StepFrame;
4034 DrawLevelField(jx, jy);
4037 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4039 // ------------------------------------------------------------------------
4040 // draw things the player is pushing, if needed
4041 // ------------------------------------------------------------------------
4043 if (!player->is_pushing || !player->is_moving)
4046 int gfx_frame = GfxFrame[jx][jy];
4048 if (!IS_MOVING(jx, jy)) // push movement already finished
4050 element = Feld[next_jx][next_jy];
4051 gfx_frame = GfxFrame[next_jx][next_jy];
4054 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4055 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4056 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4058 // draw background element under pushed element (like the Sokoban field)
4059 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4061 // this allows transparent pushing animation over non-black background
4064 DrawLevelElement(jx, jy, Back[jx][jy]);
4066 DrawLevelElement(jx, jy, EL_EMPTY);
4068 if (Back[next_jx][next_jy])
4069 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4071 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4073 else if (Back[next_jx][next_jy])
4074 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4076 int px = SCREENX(jx), py = SCREENY(jy);
4077 int pxx = (TILEX - ABS(sxx)) * dx;
4078 int pyy = (TILEY - ABS(syy)) * dy;
4081 // do not draw (EM style) pushing animation when pushing is finished
4082 // (two-tile animations usually do not contain start and end frame)
4083 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4084 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4086 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4088 // masked drawing is needed for EMC style (double) movement graphics
4089 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4090 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4093 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4095 // ------------------------------------------------------------------------
4096 // draw player himself
4097 // ------------------------------------------------------------------------
4099 int graphic = getPlayerGraphic(player, move_dir);
4101 // in the case of changed player action or direction, prevent the current
4102 // animation frame from being restarted for identical animations
4103 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4104 player->Frame = last_player_frame;
4106 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4108 if (player_is_opaque)
4109 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4111 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4113 if (SHIELD_ON(player))
4115 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4116 IMG_SHIELD_NORMAL_ACTIVE);
4117 frame = getGraphicAnimationFrame(graphic, -1);
4119 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4122 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4124 // ------------------------------------------------------------------------
4125 // draw things in front of player (active dynamite or dynabombs)
4126 // ------------------------------------------------------------------------
4128 if (IS_ACTIVE_BOMB(element))
4130 int graphic = el2img(element);
4131 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4133 if (game.emulation == EMU_SUPAPLEX)
4134 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4136 DrawGraphicThruMask(sx, sy, graphic, frame);
4139 if (player_is_moving && last_element == EL_EXPLOSION)
4141 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4142 GfxElement[last_jx][last_jy] : EL_EMPTY);
4143 int graphic = el_act2img(element, ACTION_EXPLODING);
4144 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4145 int phase = ExplodePhase[last_jx][last_jy] - 1;
4146 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4149 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4152 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4154 // ------------------------------------------------------------------------
4155 // draw elements the player is just walking/passing through/under
4156 // ------------------------------------------------------------------------
4158 if (player_is_moving)
4160 // handle the field the player is leaving ...
4161 if (IS_ACCESSIBLE_INSIDE(last_element))
4162 DrawLevelField(last_jx, last_jy);
4163 else if (IS_ACCESSIBLE_UNDER(last_element))
4164 DrawLevelFieldThruMask(last_jx, last_jy);
4167 // do not redraw accessible elements if the player is just pushing them
4168 if (!player_is_moving || !player->is_pushing)
4170 // ... and the field the player is entering
4171 if (IS_ACCESSIBLE_INSIDE(element))
4172 DrawLevelField(jx, jy);
4173 else if (IS_ACCESSIBLE_UNDER(element))
4174 DrawLevelFieldThruMask(jx, jy);
4177 MarkTileDirty(sx, sy);
4181 void DrawPlayer(struct PlayerInfo *player)
4185 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4186 DrawPlayerExt(player, i);
4189 void DrawAllPlayers(void)
4193 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4194 for (j = 0; j < MAX_PLAYERS; j++)
4195 if (stored_player[j].active)
4196 DrawPlayerExt(&stored_player[j], i);
4199 void DrawPlayerField(int x, int y)
4201 if (!IS_PLAYER(x, y))
4204 DrawPlayer(PLAYERINFO(x, y));
4207 // ----------------------------------------------------------------------------
4209 void WaitForEventToContinue(void)
4211 boolean still_wait = TRUE;
4213 if (program.headless)
4216 // simulate releasing mouse button over last gadget, if still pressed
4218 HandleGadgets(-1, -1, 0);
4220 button_status = MB_RELEASED;
4228 if (NextValidEvent(&event))
4232 case EVENT_BUTTONRELEASE:
4233 case EVENT_KEYPRESS:
4234 case SDL_CONTROLLERBUTTONDOWN:
4235 case SDL_JOYBUTTONDOWN:
4239 case EVENT_KEYRELEASE:
4240 ClearPlayerAction();
4244 HandleOtherEvents(&event);
4248 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4257 #define MAX_REQUEST_LINES 13
4258 #define MAX_REQUEST_LINE_FONT1_LEN 7
4259 #define MAX_REQUEST_LINE_FONT2_LEN 10
4261 static int RequestHandleEvents(unsigned int req_state)
4263 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4265 int width = request.width;
4266 int height = request.height;
4270 // when showing request dialog after game ended, deactivate game panel
4271 if (game_just_ended)
4272 game.panel.active = FALSE;
4274 game.request_active = TRUE;
4276 setRequestPosition(&sx, &sy, FALSE);
4278 button_status = MB_RELEASED;
4280 request_gadget_id = -1;
4285 if (game_just_ended)
4287 // the MM game engine does not use a special (scrollable) field buffer
4288 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4289 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4291 HandleGameActions();
4293 SetDrawtoField(DRAW_TO_BACKBUFFER);
4295 if (global.use_envelope_request)
4297 // copy current state of request area to middle of playfield area
4298 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4306 while (NextValidEvent(&event))
4310 case EVENT_BUTTONPRESS:
4311 case EVENT_BUTTONRELEASE:
4312 case EVENT_MOTIONNOTIFY:
4316 if (event.type == EVENT_MOTIONNOTIFY)
4321 motion_status = TRUE;
4322 mx = ((MotionEvent *) &event)->x;
4323 my = ((MotionEvent *) &event)->y;
4327 motion_status = FALSE;
4328 mx = ((ButtonEvent *) &event)->x;
4329 my = ((ButtonEvent *) &event)->y;
4330 if (event.type == EVENT_BUTTONPRESS)
4331 button_status = ((ButtonEvent *) &event)->button;
4333 button_status = MB_RELEASED;
4336 // 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:
4365 // only check clickable animations if no request gadget clicked
4366 HandleGlobalAnimClicks(mx, my, button_status, FALSE);
4373 case SDL_WINDOWEVENT:
4374 HandleWindowEvent((WindowEvent *) &event);
4377 case SDL_APP_WILLENTERBACKGROUND:
4378 case SDL_APP_DIDENTERBACKGROUND:
4379 case SDL_APP_WILLENTERFOREGROUND:
4380 case SDL_APP_DIDENTERFOREGROUND:
4381 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4384 case EVENT_KEYPRESS:
4386 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4391 if (req_state & REQ_CONFIRM)
4400 #if defined(KSYM_Rewind)
4401 case KSYM_Rewind: // for Amazon Fire TV remote
4410 #if defined(KSYM_FastForward)
4411 case KSYM_FastForward: // for Amazon Fire TV remote
4417 HandleKeysDebug(key, KEY_PRESSED);
4421 if (req_state & REQ_PLAYER)
4423 int old_player_nr = setup.network_player_nr;
4426 result = old_player_nr + 1;
4431 result = old_player_nr + 1;
4462 case EVENT_KEYRELEASE:
4463 ClearPlayerAction();
4466 case SDL_CONTROLLERBUTTONDOWN:
4467 switch (event.cbutton.button)
4469 case SDL_CONTROLLER_BUTTON_A:
4470 case SDL_CONTROLLER_BUTTON_X:
4471 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4472 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4476 case SDL_CONTROLLER_BUTTON_B:
4477 case SDL_CONTROLLER_BUTTON_Y:
4478 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4479 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4480 case SDL_CONTROLLER_BUTTON_BACK:
4485 if (req_state & REQ_PLAYER)
4487 int old_player_nr = setup.network_player_nr;
4490 result = old_player_nr + 1;
4492 switch (event.cbutton.button)
4494 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4495 case SDL_CONTROLLER_BUTTON_Y:
4499 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4500 case SDL_CONTROLLER_BUTTON_B:
4504 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4505 case SDL_CONTROLLER_BUTTON_A:
4509 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4510 case SDL_CONTROLLER_BUTTON_X:
4521 case SDL_CONTROLLERBUTTONUP:
4522 HandleJoystickEvent(&event);
4523 ClearPlayerAction();
4527 HandleOtherEvents(&event);
4532 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4534 int joy = AnyJoystick();
4536 if (joy & JOY_BUTTON_1)
4538 else if (joy & JOY_BUTTON_2)
4541 else if (AnyJoystick())
4543 int joy = AnyJoystick();
4545 if (req_state & REQ_PLAYER)
4549 else if (joy & JOY_RIGHT)
4551 else if (joy & JOY_DOWN)
4553 else if (joy & JOY_LEFT)
4558 if (game_just_ended)
4560 if (global.use_envelope_request)
4562 // copy back current state of pressed buttons inside request area
4563 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4570 game.request_active = FALSE;
4575 static boolean RequestDoor(char *text, unsigned int req_state)
4577 unsigned int old_door_state;
4578 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4579 int font_nr = FONT_TEXT_2;
4584 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4586 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4587 font_nr = FONT_TEXT_1;
4590 if (game_status == GAME_MODE_PLAYING)
4591 BlitScreenToBitmap(backbuffer);
4593 // disable deactivated drawing when quick-loading level tape recording
4594 if (tape.playing && tape.deactivate_display)
4595 TapeDeactivateDisplayOff(TRUE);
4597 SetMouseCursor(CURSOR_DEFAULT);
4599 // pause network game while waiting for request to answer
4600 if (network.enabled &&
4601 game_status == GAME_MODE_PLAYING &&
4602 !game.all_players_gone &&
4603 req_state & REQUEST_WAIT_FOR_INPUT)
4604 SendToServer_PausePlaying();
4606 old_door_state = GetDoorState();
4608 // simulate releasing mouse button over last gadget, if still pressed
4610 HandleGadgets(-1, -1, 0);
4614 // draw released gadget before proceeding
4617 if (old_door_state & DOOR_OPEN_1)
4619 CloseDoor(DOOR_CLOSE_1);
4621 // save old door content
4622 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4623 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4626 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4627 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4629 // clear door drawing field
4630 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4632 // force DOOR font inside door area
4633 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4635 // write text for request
4636 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4638 char text_line[max_request_line_len + 1];
4644 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4646 tc = *(text_ptr + tx);
4647 // if (!tc || tc == ' ')
4648 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4652 if ((tc == '?' || tc == '!') && tl == 0)
4662 strncpy(text_line, text_ptr, tl);
4665 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4666 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4667 text_line, font_nr);
4669 text_ptr += tl + (tc == ' ' ? 1 : 0);
4670 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4675 if (req_state & REQ_ASK)
4677 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4678 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4680 else if (req_state & REQ_CONFIRM)
4682 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4684 else if (req_state & REQ_PLAYER)
4686 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4687 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4688 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4689 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4692 // copy request gadgets to door backbuffer
4693 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4695 OpenDoor(DOOR_OPEN_1);
4697 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4699 if (game_status == GAME_MODE_PLAYING)
4701 SetPanelBackground();
4702 SetDrawBackgroundMask(REDRAW_DOOR_1);
4706 SetDrawBackgroundMask(REDRAW_FIELD);
4712 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4714 // ---------- handle request buttons ----------
4715 result = RequestHandleEvents(req_state);
4719 if (!(req_state & REQ_STAY_OPEN))
4721 CloseDoor(DOOR_CLOSE_1);
4723 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4724 (req_state & REQ_REOPEN))
4725 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4730 if (game_status == GAME_MODE_PLAYING)
4732 SetPanelBackground();
4733 SetDrawBackgroundMask(REDRAW_DOOR_1);
4737 SetDrawBackgroundMask(REDRAW_FIELD);
4740 // continue network game after request
4741 if (network.enabled &&
4742 game_status == GAME_MODE_PLAYING &&
4743 !game.all_players_gone &&
4744 req_state & REQUEST_WAIT_FOR_INPUT)
4745 SendToServer_ContinuePlaying();
4747 // restore deactivated drawing when quick-loading level tape recording
4748 if (tape.playing && tape.deactivate_display)
4749 TapeDeactivateDisplayOn();
4754 static boolean RequestEnvelope(char *text, unsigned int req_state)
4758 if (game_status == GAME_MODE_PLAYING)
4759 BlitScreenToBitmap(backbuffer);
4761 // disable deactivated drawing when quick-loading level tape recording
4762 if (tape.playing && tape.deactivate_display)
4763 TapeDeactivateDisplayOff(TRUE);
4765 SetMouseCursor(CURSOR_DEFAULT);
4767 // pause network game while waiting for request to answer
4768 if (network.enabled &&
4769 game_status == GAME_MODE_PLAYING &&
4770 !game.all_players_gone &&
4771 req_state & REQUEST_WAIT_FOR_INPUT)
4772 SendToServer_PausePlaying();
4774 // simulate releasing mouse button over last gadget, if still pressed
4776 HandleGadgets(-1, -1, 0);
4780 // (replace with setting corresponding request background)
4781 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4782 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4784 // clear door drawing field
4785 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4787 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4789 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4791 if (game_status == GAME_MODE_PLAYING)
4793 SetPanelBackground();
4794 SetDrawBackgroundMask(REDRAW_DOOR_1);
4798 SetDrawBackgroundMask(REDRAW_FIELD);
4804 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4806 // ---------- handle request buttons ----------
4807 result = RequestHandleEvents(req_state);
4811 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4815 if (game_status == GAME_MODE_PLAYING)
4817 SetPanelBackground();
4818 SetDrawBackgroundMask(REDRAW_DOOR_1);
4822 SetDrawBackgroundMask(REDRAW_FIELD);
4825 // continue network game after request
4826 if (network.enabled &&
4827 game_status == GAME_MODE_PLAYING &&
4828 !game.all_players_gone &&
4829 req_state & REQUEST_WAIT_FOR_INPUT)
4830 SendToServer_ContinuePlaying();
4832 // restore deactivated drawing when quick-loading level tape recording
4833 if (tape.playing && tape.deactivate_display)
4834 TapeDeactivateDisplayOn();
4839 boolean Request(char *text, unsigned int req_state)
4841 boolean overlay_enabled = GetOverlayEnabled();
4844 SetOverlayEnabled(FALSE);
4846 if (global.use_envelope_request)
4847 result = RequestEnvelope(text, req_state);
4849 result = RequestDoor(text, req_state);
4851 SetOverlayEnabled(overlay_enabled);
4856 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4858 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4859 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4862 if (dpo1->sort_priority != dpo2->sort_priority)
4863 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4865 compare_result = dpo1->nr - dpo2->nr;
4867 return compare_result;
4870 void InitGraphicCompatibilityInfo_Doors(void)
4876 struct DoorInfo *door;
4880 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4881 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4883 { -1, -1, -1, NULL }
4885 struct Rect door_rect_list[] =
4887 { DX, DY, DXSIZE, DYSIZE },
4888 { VX, VY, VXSIZE, VYSIZE }
4892 for (i = 0; doors[i].door_token != -1; i++)
4894 int door_token = doors[i].door_token;
4895 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4896 int part_1 = doors[i].part_1;
4897 int part_8 = doors[i].part_8;
4898 int part_2 = part_1 + 1;
4899 int part_3 = part_1 + 2;
4900 struct DoorInfo *door = doors[i].door;
4901 struct Rect *door_rect = &door_rect_list[door_index];
4902 boolean door_gfx_redefined = FALSE;
4904 // check if any door part graphic definitions have been redefined
4906 for (j = 0; door_part_controls[j].door_token != -1; j++)
4908 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4909 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4911 if (dpc->door_token == door_token && fi->redefined)
4912 door_gfx_redefined = TRUE;
4915 // check for old-style door graphic/animation modifications
4917 if (!door_gfx_redefined)
4919 if (door->anim_mode & ANIM_STATIC_PANEL)
4921 door->panel.step_xoffset = 0;
4922 door->panel.step_yoffset = 0;
4925 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4927 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4928 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4929 int num_door_steps, num_panel_steps;
4931 // remove door part graphics other than the two default wings
4933 for (j = 0; door_part_controls[j].door_token != -1; j++)
4935 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4936 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4938 if (dpc->graphic >= part_3 &&
4939 dpc->graphic <= part_8)
4943 // set graphics and screen positions of the default wings
4945 g_part_1->width = door_rect->width;
4946 g_part_1->height = door_rect->height;
4947 g_part_2->width = door_rect->width;
4948 g_part_2->height = door_rect->height;
4949 g_part_2->src_x = door_rect->width;
4950 g_part_2->src_y = g_part_1->src_y;
4952 door->part_2.x = door->part_1.x;
4953 door->part_2.y = door->part_1.y;
4955 if (door->width != -1)
4957 g_part_1->width = door->width;
4958 g_part_2->width = door->width;
4960 // special treatment for graphics and screen position of right wing
4961 g_part_2->src_x += door_rect->width - door->width;
4962 door->part_2.x += door_rect->width - door->width;
4965 if (door->height != -1)
4967 g_part_1->height = door->height;
4968 g_part_2->height = door->height;
4970 // special treatment for graphics and screen position of bottom wing
4971 g_part_2->src_y += door_rect->height - door->height;
4972 door->part_2.y += door_rect->height - door->height;
4975 // set animation delays for the default wings and panels
4977 door->part_1.step_delay = door->step_delay;
4978 door->part_2.step_delay = door->step_delay;
4979 door->panel.step_delay = door->step_delay;
4981 // set animation draw order for the default wings
4983 door->part_1.sort_priority = 2; // draw left wing over ...
4984 door->part_2.sort_priority = 1; // ... right wing
4986 // set animation draw offset for the default wings
4988 if (door->anim_mode & ANIM_HORIZONTAL)
4990 door->part_1.step_xoffset = door->step_offset;
4991 door->part_1.step_yoffset = 0;
4992 door->part_2.step_xoffset = door->step_offset * -1;
4993 door->part_2.step_yoffset = 0;
4995 num_door_steps = g_part_1->width / door->step_offset;
4997 else // ANIM_VERTICAL
4999 door->part_1.step_xoffset = 0;
5000 door->part_1.step_yoffset = door->step_offset;
5001 door->part_2.step_xoffset = 0;
5002 door->part_2.step_yoffset = door->step_offset * -1;
5004 num_door_steps = g_part_1->height / door->step_offset;
5007 // set animation draw offset for the default panels
5009 if (door->step_offset > 1)
5011 num_panel_steps = 2 * door_rect->height / door->step_offset;
5012 door->panel.start_step = num_panel_steps - num_door_steps;
5013 door->panel.start_step_closing = door->panel.start_step;
5017 num_panel_steps = door_rect->height / door->step_offset;
5018 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5019 door->panel.start_step_closing = door->panel.start_step;
5020 door->panel.step_delay *= 2;
5027 void InitDoors(void)
5031 for (i = 0; door_part_controls[i].door_token != -1; i++)
5033 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5034 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5036 // initialize "start_step_opening" and "start_step_closing", if needed
5037 if (dpc->pos->start_step_opening == 0 &&
5038 dpc->pos->start_step_closing == 0)
5040 // dpc->pos->start_step_opening = dpc->pos->start_step;
5041 dpc->pos->start_step_closing = dpc->pos->start_step;
5044 // fill structure for door part draw order (sorted below)
5046 dpo->sort_priority = dpc->pos->sort_priority;
5049 // sort door part controls according to sort_priority and graphic number
5050 qsort(door_part_order, MAX_DOOR_PARTS,
5051 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5054 unsigned int OpenDoor(unsigned int door_state)
5056 if (door_state & DOOR_COPY_BACK)
5058 if (door_state & DOOR_OPEN_1)
5059 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5060 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5062 if (door_state & DOOR_OPEN_2)
5063 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5064 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5066 door_state &= ~DOOR_COPY_BACK;
5069 return MoveDoor(door_state);
5072 unsigned int CloseDoor(unsigned int door_state)
5074 unsigned int old_door_state = GetDoorState();
5076 if (!(door_state & DOOR_NO_COPY_BACK))
5078 if (old_door_state & DOOR_OPEN_1)
5079 BlitBitmap(backbuffer, bitmap_db_door_1,
5080 DX, DY, DXSIZE, DYSIZE, 0, 0);
5082 if (old_door_state & DOOR_OPEN_2)
5083 BlitBitmap(backbuffer, bitmap_db_door_2,
5084 VX, VY, VXSIZE, VYSIZE, 0, 0);
5086 door_state &= ~DOOR_NO_COPY_BACK;
5089 return MoveDoor(door_state);
5092 unsigned int GetDoorState(void)
5094 return MoveDoor(DOOR_GET_STATE);
5097 unsigned int SetDoorState(unsigned int door_state)
5099 return MoveDoor(door_state | DOOR_SET_STATE);
5102 static int euclid(int a, int b)
5104 return (b ? euclid(b, a % b) : a);
5107 unsigned int MoveDoor(unsigned int door_state)
5109 struct Rect door_rect_list[] =
5111 { DX, DY, DXSIZE, DYSIZE },
5112 { VX, VY, VXSIZE, VYSIZE }
5114 static int door1 = DOOR_CLOSE_1;
5115 static int door2 = DOOR_CLOSE_2;
5116 unsigned int door_delay = 0;
5117 unsigned int door_delay_value;
5120 if (door_state == DOOR_GET_STATE)
5121 return (door1 | door2);
5123 if (door_state & DOOR_SET_STATE)
5125 if (door_state & DOOR_ACTION_1)
5126 door1 = door_state & DOOR_ACTION_1;
5127 if (door_state & DOOR_ACTION_2)
5128 door2 = door_state & DOOR_ACTION_2;
5130 return (door1 | door2);
5133 if (!(door_state & DOOR_FORCE_REDRAW))
5135 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5136 door_state &= ~DOOR_OPEN_1;
5137 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5138 door_state &= ~DOOR_CLOSE_1;
5139 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5140 door_state &= ~DOOR_OPEN_2;
5141 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5142 door_state &= ~DOOR_CLOSE_2;
5145 if (global.autoplay_leveldir)
5147 door_state |= DOOR_NO_DELAY;
5148 door_state &= ~DOOR_CLOSE_ALL;
5151 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5152 door_state |= DOOR_NO_DELAY;
5154 if (door_state & DOOR_ACTION)
5156 boolean door_panel_drawn[NUM_DOORS];
5157 boolean panel_has_doors[NUM_DOORS];
5158 boolean door_part_skip[MAX_DOOR_PARTS];
5159 boolean door_part_done[MAX_DOOR_PARTS];
5160 boolean door_part_done_all;
5161 int num_steps[MAX_DOOR_PARTS];
5162 int max_move_delay = 0; // delay for complete animations of all doors
5163 int max_step_delay = 0; // delay (ms) between two animation frames
5164 int num_move_steps = 0; // number of animation steps for all doors
5165 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5166 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5167 int current_move_delay = 0;
5171 for (i = 0; i < NUM_DOORS; i++)
5172 panel_has_doors[i] = FALSE;
5174 for (i = 0; i < MAX_DOOR_PARTS; i++)
5176 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5177 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5178 int door_token = dpc->door_token;
5180 door_part_done[i] = FALSE;
5181 door_part_skip[i] = (!(door_state & door_token) ||
5185 for (i = 0; i < MAX_DOOR_PARTS; i++)
5187 int nr = door_part_order[i].nr;
5188 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5189 struct DoorPartPosInfo *pos = dpc->pos;
5190 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5191 int door_token = dpc->door_token;
5192 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5193 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5194 int step_xoffset = ABS(pos->step_xoffset);
5195 int step_yoffset = ABS(pos->step_yoffset);
5196 int step_delay = pos->step_delay;
5197 int current_door_state = door_state & door_token;
5198 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5199 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5200 boolean part_opening = (is_panel ? door_closing : door_opening);
5201 int start_step = (part_opening ? pos->start_step_opening :
5202 pos->start_step_closing);
5203 float move_xsize = (step_xoffset ? g->width : 0);
5204 float move_ysize = (step_yoffset ? g->height : 0);
5205 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5206 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5207 int move_steps = (move_xsteps && move_ysteps ?
5208 MIN(move_xsteps, move_ysteps) :
5209 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5210 int move_delay = move_steps * step_delay;
5212 if (door_part_skip[nr])
5215 max_move_delay = MAX(max_move_delay, move_delay);
5216 max_step_delay = (max_step_delay == 0 ? step_delay :
5217 euclid(max_step_delay, step_delay));
5218 num_steps[nr] = move_steps;
5222 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5224 panel_has_doors[door_index] = TRUE;
5228 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5230 num_move_steps = max_move_delay / max_step_delay;
5231 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5233 door_delay_value = max_step_delay;
5235 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5237 start = num_move_steps - 1;
5241 // opening door sound has priority over simultaneously closing door
5242 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5244 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5246 if (door_state & DOOR_OPEN_1)
5247 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5248 if (door_state & DOOR_OPEN_2)
5249 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5251 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5253 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5255 if (door_state & DOOR_CLOSE_1)
5256 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5257 if (door_state & DOOR_CLOSE_2)
5258 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5262 for (k = start; k < num_move_steps; k++)
5264 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5266 door_part_done_all = TRUE;
5268 for (i = 0; i < NUM_DOORS; i++)
5269 door_panel_drawn[i] = FALSE;
5271 for (i = 0; i < MAX_DOOR_PARTS; i++)
5273 int nr = door_part_order[i].nr;
5274 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5275 struct DoorPartPosInfo *pos = dpc->pos;
5276 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5277 int door_token = dpc->door_token;
5278 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5279 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5280 boolean is_panel_and_door_has_closed = FALSE;
5281 struct Rect *door_rect = &door_rect_list[door_index];
5282 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5284 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5285 int current_door_state = door_state & door_token;
5286 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5287 boolean door_closing = !door_opening;
5288 boolean part_opening = (is_panel ? door_closing : door_opening);
5289 boolean part_closing = !part_opening;
5290 int start_step = (part_opening ? pos->start_step_opening :
5291 pos->start_step_closing);
5292 int step_delay = pos->step_delay;
5293 int step_factor = step_delay / max_step_delay;
5294 int k1 = (step_factor ? k / step_factor + 1 : k);
5295 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5296 int kk = MAX(0, k2);
5299 int src_x, src_y, src_xx, src_yy;
5300 int dst_x, dst_y, dst_xx, dst_yy;
5303 if (door_part_skip[nr])
5306 if (!(door_state & door_token))
5314 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5315 int kk_door = MAX(0, k2_door);
5316 int sync_frame = kk_door * door_delay_value;
5317 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5319 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5320 &g_src_x, &g_src_y);
5325 if (!door_panel_drawn[door_index])
5327 ClearRectangle(drawto, door_rect->x, door_rect->y,
5328 door_rect->width, door_rect->height);
5330 door_panel_drawn[door_index] = TRUE;
5333 // draw opening or closing door parts
5335 if (pos->step_xoffset < 0) // door part on right side
5338 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5341 if (dst_xx + width > door_rect->width)
5342 width = door_rect->width - dst_xx;
5344 else // door part on left side
5347 dst_xx = pos->x - kk * pos->step_xoffset;
5351 src_xx = ABS(dst_xx);
5355 width = g->width - src_xx;
5357 if (width > door_rect->width)
5358 width = door_rect->width;
5360 // printf("::: k == %d [%d] \n", k, start_step);
5363 if (pos->step_yoffset < 0) // door part on bottom side
5366 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5369 if (dst_yy + height > door_rect->height)
5370 height = door_rect->height - dst_yy;
5372 else // door part on top side
5375 dst_yy = pos->y - kk * pos->step_yoffset;
5379 src_yy = ABS(dst_yy);
5383 height = g->height - src_yy;
5386 src_x = g_src_x + src_xx;
5387 src_y = g_src_y + src_yy;
5389 dst_x = door_rect->x + dst_xx;
5390 dst_y = door_rect->y + dst_yy;
5392 is_panel_and_door_has_closed =
5395 panel_has_doors[door_index] &&
5396 k >= num_move_steps_doors_only - 1);
5398 if (width >= 0 && width <= g->width &&
5399 height >= 0 && height <= g->height &&
5400 !is_panel_and_door_has_closed)
5402 if (is_panel || !pos->draw_masked)
5403 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5406 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5410 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5412 if ((part_opening && (width < 0 || height < 0)) ||
5413 (part_closing && (width >= g->width && height >= g->height)))
5414 door_part_done[nr] = TRUE;
5416 // continue door part animations, but not panel after door has closed
5417 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5418 door_part_done_all = FALSE;
5421 if (!(door_state & DOOR_NO_DELAY))
5425 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5427 current_move_delay += max_step_delay;
5429 // prevent OS (Windows) from complaining about program not responding
5433 if (door_part_done_all)
5437 if (!(door_state & DOOR_NO_DELAY))
5439 // wait for specified door action post delay
5440 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5441 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5442 else if (door_state & DOOR_ACTION_1)
5443 door_delay_value = door_1.post_delay;
5444 else if (door_state & DOOR_ACTION_2)
5445 door_delay_value = door_2.post_delay;
5447 while (!DelayReached(&door_delay, door_delay_value))
5452 if (door_state & DOOR_ACTION_1)
5453 door1 = door_state & DOOR_ACTION_1;
5454 if (door_state & DOOR_ACTION_2)
5455 door2 = door_state & DOOR_ACTION_2;
5457 // draw masked border over door area
5458 DrawMaskedBorder(REDRAW_DOOR_1);
5459 DrawMaskedBorder(REDRAW_DOOR_2);
5461 ClearAutoRepeatKeyEvents();
5463 return (door1 | door2);
5466 static boolean useSpecialEditorDoor(void)
5468 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5469 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5471 // do not draw special editor door if editor border defined or redefined
5472 if (graphic_info[graphic].bitmap != NULL || redefined)
5475 // do not draw special editor door if global border defined to be empty
5476 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5479 // do not draw special editor door if viewport definitions do not match
5483 EY + EYSIZE != VY + VYSIZE)
5489 void DrawSpecialEditorDoor(void)
5491 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5492 int top_border_width = gfx1->width;
5493 int top_border_height = gfx1->height;
5494 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5495 int ex = EX - outer_border;
5496 int ey = EY - outer_border;
5497 int vy = VY - outer_border;
5498 int exsize = EXSIZE + 2 * outer_border;
5500 if (!useSpecialEditorDoor())
5503 // draw bigger level editor toolbox window
5504 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5505 top_border_width, top_border_height, ex, ey - top_border_height);
5506 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5507 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5509 redraw_mask |= REDRAW_ALL;
5512 void UndrawSpecialEditorDoor(void)
5514 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5515 int top_border_width = gfx1->width;
5516 int top_border_height = gfx1->height;
5517 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5518 int ex = EX - outer_border;
5519 int ey = EY - outer_border;
5520 int ey_top = ey - top_border_height;
5521 int exsize = EXSIZE + 2 * outer_border;
5522 int eysize = EYSIZE + 2 * outer_border;
5524 if (!useSpecialEditorDoor())
5527 // draw normal tape recorder window
5528 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5530 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5531 ex, ey_top, top_border_width, top_border_height,
5533 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5534 ex, ey, exsize, eysize, ex, ey);
5538 // if screen background is set to "[NONE]", clear editor toolbox window
5539 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5540 ClearRectangle(drawto, ex, ey, exsize, eysize);
5543 redraw_mask |= REDRAW_ALL;
5547 // ---------- new tool button stuff -------------------------------------------
5552 struct TextPosInfo *pos;
5555 } toolbutton_info[NUM_TOOL_BUTTONS] =
5558 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5559 TOOL_CTRL_ID_YES, "yes"
5562 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5563 TOOL_CTRL_ID_NO, "no"
5566 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5567 TOOL_CTRL_ID_CONFIRM, "confirm"
5570 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5571 TOOL_CTRL_ID_PLAYER_1, "player 1"
5574 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5575 TOOL_CTRL_ID_PLAYER_2, "player 2"
5578 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5579 TOOL_CTRL_ID_PLAYER_3, "player 3"
5582 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5583 TOOL_CTRL_ID_PLAYER_4, "player 4"
5587 void CreateToolButtons(void)
5591 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5593 int graphic = toolbutton_info[i].graphic;
5594 struct GraphicInfo *gfx = &graphic_info[graphic];
5595 struct TextPosInfo *pos = toolbutton_info[i].pos;
5596 struct GadgetInfo *gi;
5597 Bitmap *deco_bitmap = None;
5598 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5599 unsigned int event_mask = GD_EVENT_RELEASED;
5602 int gd_x = gfx->src_x;
5603 int gd_y = gfx->src_y;
5604 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5605 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5610 if (global.use_envelope_request)
5612 setRequestPosition(&dx, &dy, TRUE);
5614 // check if request buttons are outside of envelope and fix, if needed
5615 if (x < 0 || x + gfx->width > request.width ||
5616 y < 0 || y + gfx->height > request.height)
5618 if (id == TOOL_CTRL_ID_YES)
5621 y = request.height - 2 * request.border_size - gfx->height;
5623 else if (id == TOOL_CTRL_ID_NO)
5625 x = request.width - 2 * request.border_size - gfx->width;
5626 y = request.height - 2 * request.border_size - gfx->height;
5628 else if (id == TOOL_CTRL_ID_CONFIRM)
5630 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5631 y = request.height - 2 * request.border_size - gfx->height;
5633 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5635 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5637 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5638 y = request.height - 2 * request.border_size - gfx->height * 2;
5640 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5641 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5646 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5648 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5650 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5651 pos->size, &deco_bitmap, &deco_x, &deco_y);
5652 deco_xpos = (gfx->width - pos->size) / 2;
5653 deco_ypos = (gfx->height - pos->size) / 2;
5656 gi = CreateGadget(GDI_CUSTOM_ID, id,
5657 GDI_IMAGE_ID, graphic,
5658 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5661 GDI_WIDTH, gfx->width,
5662 GDI_HEIGHT, gfx->height,
5663 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5664 GDI_STATE, GD_BUTTON_UNPRESSED,
5665 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5666 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5667 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5668 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5669 GDI_DECORATION_SIZE, pos->size, pos->size,
5670 GDI_DECORATION_SHIFTING, 1, 1,
5671 GDI_DIRECT_DRAW, FALSE,
5672 GDI_EVENT_MASK, event_mask,
5673 GDI_CALLBACK_ACTION, HandleToolButtons,
5677 Error(ERR_EXIT, "cannot create gadget");
5679 tool_gadget[id] = gi;
5683 void FreeToolButtons(void)
5687 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5688 FreeGadget(tool_gadget[i]);
5691 static void UnmapToolButtons(void)
5695 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5696 UnmapGadget(tool_gadget[i]);
5699 static void HandleToolButtons(struct GadgetInfo *gi)
5701 request_gadget_id = gi->custom_id;
5704 static struct Mapping_EM_to_RND_object
5707 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5708 boolean is_backside; // backside of moving element
5714 em_object_mapping_list[] =
5717 Xblank, TRUE, FALSE,
5721 Yacid_splash_eB, FALSE, FALSE,
5722 EL_ACID_SPLASH_RIGHT, -1, -1
5725 Yacid_splash_wB, FALSE, FALSE,
5726 EL_ACID_SPLASH_LEFT, -1, -1
5729 #ifdef EM_ENGINE_BAD_ROLL
5731 Xstone_force_e, FALSE, FALSE,
5732 EL_ROCK, -1, MV_BIT_RIGHT
5735 Xstone_force_w, FALSE, FALSE,
5736 EL_ROCK, -1, MV_BIT_LEFT
5739 Xnut_force_e, FALSE, FALSE,
5740 EL_NUT, -1, MV_BIT_RIGHT
5743 Xnut_force_w, FALSE, FALSE,
5744 EL_NUT, -1, MV_BIT_LEFT
5747 Xspring_force_e, FALSE, FALSE,
5748 EL_SPRING, -1, MV_BIT_RIGHT
5751 Xspring_force_w, FALSE, FALSE,
5752 EL_SPRING, -1, MV_BIT_LEFT
5755 Xemerald_force_e, FALSE, FALSE,
5756 EL_EMERALD, -1, MV_BIT_RIGHT
5759 Xemerald_force_w, FALSE, FALSE,
5760 EL_EMERALD, -1, MV_BIT_LEFT
5763 Xdiamond_force_e, FALSE, FALSE,
5764 EL_DIAMOND, -1, MV_BIT_RIGHT
5767 Xdiamond_force_w, FALSE, FALSE,
5768 EL_DIAMOND, -1, MV_BIT_LEFT
5771 Xbomb_force_e, FALSE, FALSE,
5772 EL_BOMB, -1, MV_BIT_RIGHT
5775 Xbomb_force_w, FALSE, FALSE,
5776 EL_BOMB, -1, MV_BIT_LEFT
5778 #endif // EM_ENGINE_BAD_ROLL
5781 Xstone, TRUE, FALSE,
5785 Xstone_pause, FALSE, FALSE,
5789 Xstone_fall, FALSE, FALSE,
5793 Ystone_s, FALSE, FALSE,
5794 EL_ROCK, ACTION_FALLING, -1
5797 Ystone_sB, FALSE, TRUE,
5798 EL_ROCK, ACTION_FALLING, -1
5801 Ystone_e, FALSE, FALSE,
5802 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5805 Ystone_eB, FALSE, TRUE,
5806 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5809 Ystone_w, FALSE, FALSE,
5810 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5813 Ystone_wB, FALSE, TRUE,
5814 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5821 Xnut_pause, FALSE, FALSE,
5825 Xnut_fall, FALSE, FALSE,
5829 Ynut_s, FALSE, FALSE,
5830 EL_NUT, ACTION_FALLING, -1
5833 Ynut_sB, FALSE, TRUE,
5834 EL_NUT, ACTION_FALLING, -1
5837 Ynut_e, FALSE, FALSE,
5838 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5841 Ynut_eB, FALSE, TRUE,
5842 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5845 Ynut_w, FALSE, FALSE,
5846 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5849 Ynut_wB, FALSE, TRUE,
5850 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5853 Xbug_n, TRUE, FALSE,
5857 Xbug_e, TRUE, FALSE,
5858 EL_BUG_RIGHT, -1, -1
5861 Xbug_s, TRUE, FALSE,
5865 Xbug_w, TRUE, FALSE,
5869 Xbug_gon, FALSE, FALSE,
5873 Xbug_goe, FALSE, FALSE,
5874 EL_BUG_RIGHT, -1, -1
5877 Xbug_gos, FALSE, FALSE,
5881 Xbug_gow, FALSE, FALSE,
5885 Ybug_n, FALSE, FALSE,
5886 EL_BUG, ACTION_MOVING, MV_BIT_UP
5889 Ybug_nB, FALSE, TRUE,
5890 EL_BUG, ACTION_MOVING, MV_BIT_UP
5893 Ybug_e, FALSE, FALSE,
5894 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5897 Ybug_eB, FALSE, TRUE,
5898 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5901 Ybug_s, FALSE, FALSE,
5902 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5905 Ybug_sB, FALSE, TRUE,
5906 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5909 Ybug_w, FALSE, FALSE,
5910 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5913 Ybug_wB, FALSE, TRUE,
5914 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5917 Ybug_w_n, FALSE, FALSE,
5918 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5921 Ybug_n_e, FALSE, FALSE,
5922 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5925 Ybug_e_s, FALSE, FALSE,
5926 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5929 Ybug_s_w, FALSE, FALSE,
5930 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5933 Ybug_e_n, FALSE, FALSE,
5934 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5937 Ybug_s_e, FALSE, FALSE,
5938 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5941 Ybug_w_s, FALSE, FALSE,
5942 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5945 Ybug_n_w, FALSE, FALSE,
5946 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5949 Ybug_stone, FALSE, FALSE,
5950 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5953 Ybug_spring, FALSE, FALSE,
5954 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5957 Xtank_n, TRUE, FALSE,
5958 EL_SPACESHIP_UP, -1, -1
5961 Xtank_e, TRUE, FALSE,
5962 EL_SPACESHIP_RIGHT, -1, -1
5965 Xtank_s, TRUE, FALSE,
5966 EL_SPACESHIP_DOWN, -1, -1
5969 Xtank_w, TRUE, FALSE,
5970 EL_SPACESHIP_LEFT, -1, -1
5973 Xtank_gon, FALSE, FALSE,
5974 EL_SPACESHIP_UP, -1, -1
5977 Xtank_goe, FALSE, FALSE,
5978 EL_SPACESHIP_RIGHT, -1, -1
5981 Xtank_gos, FALSE, FALSE,
5982 EL_SPACESHIP_DOWN, -1, -1
5985 Xtank_gow, FALSE, FALSE,
5986 EL_SPACESHIP_LEFT, -1, -1
5989 Ytank_n, FALSE, FALSE,
5990 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5993 Ytank_nB, FALSE, TRUE,
5994 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5997 Ytank_e, FALSE, FALSE,
5998 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6001 Ytank_eB, FALSE, TRUE,
6002 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
6005 Ytank_s, FALSE, FALSE,
6006 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6009 Ytank_sB, FALSE, TRUE,
6010 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6013 Ytank_w, FALSE, FALSE,
6014 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6017 Ytank_wB, FALSE, TRUE,
6018 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6021 Ytank_w_n, FALSE, FALSE,
6022 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6025 Ytank_n_e, FALSE, FALSE,
6026 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6029 Ytank_e_s, FALSE, FALSE,
6030 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6033 Ytank_s_w, FALSE, FALSE,
6034 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6037 Ytank_e_n, FALSE, FALSE,
6038 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6041 Ytank_s_e, FALSE, FALSE,
6042 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6045 Ytank_w_s, FALSE, FALSE,
6046 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6049 Ytank_n_w, FALSE, FALSE,
6050 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6053 Ytank_stone, FALSE, FALSE,
6054 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6057 Ytank_spring, FALSE, FALSE,
6058 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6061 Xandroid, TRUE, FALSE,
6062 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6065 Xandroid_1_n, FALSE, FALSE,
6066 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6069 Xandroid_2_n, FALSE, FALSE,
6070 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6073 Xandroid_1_e, FALSE, FALSE,
6074 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6077 Xandroid_2_e, FALSE, FALSE,
6078 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6081 Xandroid_1_w, FALSE, FALSE,
6082 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6085 Xandroid_2_w, FALSE, FALSE,
6086 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6089 Xandroid_1_s, FALSE, FALSE,
6090 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6093 Xandroid_2_s, FALSE, FALSE,
6094 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6097 Yandroid_n, FALSE, FALSE,
6098 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6101 Yandroid_nB, FALSE, TRUE,
6102 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6105 Yandroid_ne, FALSE, FALSE,
6106 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6109 Yandroid_neB, FALSE, TRUE,
6110 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6113 Yandroid_e, FALSE, FALSE,
6114 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6117 Yandroid_eB, FALSE, TRUE,
6118 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6121 Yandroid_se, FALSE, FALSE,
6122 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6125 Yandroid_seB, FALSE, TRUE,
6126 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6129 Yandroid_s, FALSE, FALSE,
6130 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6133 Yandroid_sB, FALSE, TRUE,
6134 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6137 Yandroid_sw, FALSE, FALSE,
6138 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6141 Yandroid_swB, FALSE, TRUE,
6142 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6145 Yandroid_w, FALSE, FALSE,
6146 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6149 Yandroid_wB, FALSE, TRUE,
6150 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6153 Yandroid_nw, FALSE, FALSE,
6154 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6157 Yandroid_nwB, FALSE, TRUE,
6158 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6161 Xspring, TRUE, FALSE,
6165 Xspring_pause, FALSE, FALSE,
6169 Xspring_e, FALSE, FALSE,
6173 Xspring_w, FALSE, FALSE,
6177 Xspring_fall, FALSE, FALSE,
6181 Yspring_s, FALSE, FALSE,
6182 EL_SPRING, ACTION_FALLING, -1
6185 Yspring_sB, FALSE, TRUE,
6186 EL_SPRING, ACTION_FALLING, -1
6189 Yspring_e, FALSE, FALSE,
6190 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6193 Yspring_eB, FALSE, TRUE,
6194 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6197 Yspring_w, FALSE, FALSE,
6198 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6201 Yspring_wB, FALSE, TRUE,
6202 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6205 Yspring_kill_e, FALSE, FALSE,
6206 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6209 Yspring_kill_eB, FALSE, TRUE,
6210 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6213 Yspring_kill_w, FALSE, FALSE,
6214 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6217 Yspring_kill_wB, FALSE, TRUE,
6218 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6221 Xeater_n, TRUE, FALSE,
6222 EL_YAMYAM_UP, -1, -1
6225 Xeater_e, TRUE, FALSE,
6226 EL_YAMYAM_RIGHT, -1, -1
6229 Xeater_w, TRUE, FALSE,
6230 EL_YAMYAM_LEFT, -1, -1
6233 Xeater_s, TRUE, FALSE,
6234 EL_YAMYAM_DOWN, -1, -1
6237 Yeater_n, FALSE, FALSE,
6238 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6241 Yeater_nB, FALSE, TRUE,
6242 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6245 Yeater_e, FALSE, FALSE,
6246 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6249 Yeater_eB, FALSE, TRUE,
6250 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6253 Yeater_s, FALSE, FALSE,
6254 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6257 Yeater_sB, FALSE, TRUE,
6258 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6261 Yeater_w, FALSE, FALSE,
6262 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6265 Yeater_wB, FALSE, TRUE,
6266 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6269 Yeater_stone, FALSE, FALSE,
6270 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6273 Yeater_spring, FALSE, FALSE,
6274 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6277 Xalien, TRUE, FALSE,
6281 Xalien_pause, FALSE, FALSE,
6285 Yalien_n, FALSE, FALSE,
6286 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6289 Yalien_nB, FALSE, TRUE,
6290 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6293 Yalien_e, FALSE, FALSE,
6294 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6297 Yalien_eB, FALSE, TRUE,
6298 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6301 Yalien_s, FALSE, FALSE,
6302 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6305 Yalien_sB, FALSE, TRUE,
6306 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6309 Yalien_w, FALSE, FALSE,
6310 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6313 Yalien_wB, FALSE, TRUE,
6314 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6317 Yalien_stone, FALSE, FALSE,
6318 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6321 Yalien_spring, FALSE, FALSE,
6322 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6325 Xemerald, TRUE, FALSE,
6329 Xemerald_pause, FALSE, FALSE,
6333 Xemerald_fall, FALSE, FALSE,
6337 Xemerald_shine, FALSE, FALSE,
6338 EL_EMERALD, ACTION_TWINKLING, -1
6341 Yemerald_s, FALSE, FALSE,
6342 EL_EMERALD, ACTION_FALLING, -1
6345 Yemerald_sB, FALSE, TRUE,
6346 EL_EMERALD, ACTION_FALLING, -1
6349 Yemerald_e, FALSE, FALSE,
6350 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6353 Yemerald_eB, FALSE, TRUE,
6354 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6357 Yemerald_w, FALSE, FALSE,
6358 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6361 Yemerald_wB, FALSE, TRUE,
6362 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6365 Yemerald_eat, FALSE, FALSE,
6366 EL_EMERALD, ACTION_COLLECTING, -1
6369 Yemerald_stone, FALSE, FALSE,
6370 EL_NUT, ACTION_BREAKING, -1
6373 Xdiamond, TRUE, FALSE,
6377 Xdiamond_pause, FALSE, FALSE,
6381 Xdiamond_fall, FALSE, FALSE,
6385 Xdiamond_shine, FALSE, FALSE,
6386 EL_DIAMOND, ACTION_TWINKLING, -1
6389 Ydiamond_s, FALSE, FALSE,
6390 EL_DIAMOND, ACTION_FALLING, -1
6393 Ydiamond_sB, FALSE, TRUE,
6394 EL_DIAMOND, ACTION_FALLING, -1
6397 Ydiamond_e, FALSE, FALSE,
6398 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6401 Ydiamond_eB, FALSE, TRUE,
6402 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6405 Ydiamond_w, FALSE, FALSE,
6406 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6409 Ydiamond_wB, FALSE, TRUE,
6410 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6413 Ydiamond_eat, FALSE, FALSE,
6414 EL_DIAMOND, ACTION_COLLECTING, -1
6417 Ydiamond_stone, FALSE, FALSE,
6418 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6421 Xdrip_fall, TRUE, FALSE,
6422 EL_AMOEBA_DROP, -1, -1
6425 Xdrip_stretch, FALSE, FALSE,
6426 EL_AMOEBA_DROP, ACTION_FALLING, -1
6429 Xdrip_stretchB, FALSE, TRUE,
6430 EL_AMOEBA_DROP, ACTION_FALLING, -1
6433 Xdrip_eat, FALSE, FALSE,
6434 EL_AMOEBA_DROP, ACTION_GROWING, -1
6437 Ydrip_s1, FALSE, FALSE,
6438 EL_AMOEBA_DROP, ACTION_FALLING, -1
6441 Ydrip_s1B, FALSE, TRUE,
6442 EL_AMOEBA_DROP, ACTION_FALLING, -1
6445 Ydrip_s2, FALSE, FALSE,
6446 EL_AMOEBA_DROP, ACTION_FALLING, -1
6449 Ydrip_s2B, FALSE, TRUE,
6450 EL_AMOEBA_DROP, ACTION_FALLING, -1
6457 Xbomb_pause, FALSE, FALSE,
6461 Xbomb_fall, FALSE, FALSE,
6465 Ybomb_s, FALSE, FALSE,
6466 EL_BOMB, ACTION_FALLING, -1
6469 Ybomb_sB, FALSE, TRUE,
6470 EL_BOMB, ACTION_FALLING, -1
6473 Ybomb_e, FALSE, FALSE,
6474 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6477 Ybomb_eB, FALSE, TRUE,
6478 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6481 Ybomb_w, FALSE, FALSE,
6482 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6485 Ybomb_wB, FALSE, TRUE,
6486 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6489 Ybomb_eat, FALSE, FALSE,
6490 EL_BOMB, ACTION_ACTIVATING, -1
6493 Xballoon, TRUE, FALSE,
6497 Yballoon_n, FALSE, FALSE,
6498 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6501 Yballoon_nB, FALSE, TRUE,
6502 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6505 Yballoon_e, FALSE, FALSE,
6506 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6509 Yballoon_eB, FALSE, TRUE,
6510 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6513 Yballoon_s, FALSE, FALSE,
6514 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6517 Yballoon_sB, FALSE, TRUE,
6518 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6521 Yballoon_w, FALSE, FALSE,
6522 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6525 Yballoon_wB, FALSE, TRUE,
6526 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6529 Xgrass, TRUE, FALSE,
6530 EL_EMC_GRASS, -1, -1
6533 Ygrass_nB, FALSE, FALSE,
6534 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6537 Ygrass_eB, FALSE, FALSE,
6538 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6541 Ygrass_sB, FALSE, FALSE,
6542 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6545 Ygrass_wB, FALSE, FALSE,
6546 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6553 Ydirt_nB, FALSE, FALSE,
6554 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6557 Ydirt_eB, FALSE, FALSE,
6558 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6561 Ydirt_sB, FALSE, FALSE,
6562 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6565 Ydirt_wB, FALSE, FALSE,
6566 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6569 Xacid_ne, TRUE, FALSE,
6570 EL_ACID_POOL_TOPRIGHT, -1, -1
6573 Xacid_se, TRUE, FALSE,
6574 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6577 Xacid_s, TRUE, FALSE,
6578 EL_ACID_POOL_BOTTOM, -1, -1
6581 Xacid_sw, TRUE, FALSE,
6582 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6585 Xacid_nw, TRUE, FALSE,
6586 EL_ACID_POOL_TOPLEFT, -1, -1
6589 Xacid_1, TRUE, FALSE,
6593 Xacid_2, FALSE, FALSE,
6597 Xacid_3, FALSE, FALSE,
6601 Xacid_4, FALSE, FALSE,
6605 Xacid_5, FALSE, FALSE,
6609 Xacid_6, FALSE, FALSE,
6613 Xacid_7, FALSE, FALSE,
6617 Xacid_8, FALSE, FALSE,
6621 Xball_1, TRUE, FALSE,
6622 EL_EMC_MAGIC_BALL, -1, -1
6625 Xball_1B, FALSE, FALSE,
6626 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6629 Xball_2, FALSE, FALSE,
6630 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6633 Xball_2B, FALSE, FALSE,
6634 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6637 Yball_eat, FALSE, FALSE,
6638 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6641 Ykey_1_eat, FALSE, FALSE,
6642 EL_EM_KEY_1, ACTION_COLLECTING, -1
6645 Ykey_2_eat, FALSE, FALSE,
6646 EL_EM_KEY_2, ACTION_COLLECTING, -1
6649 Ykey_3_eat, FALSE, FALSE,
6650 EL_EM_KEY_3, ACTION_COLLECTING, -1
6653 Ykey_4_eat, FALSE, FALSE,
6654 EL_EM_KEY_4, ACTION_COLLECTING, -1
6657 Ykey_5_eat, FALSE, FALSE,
6658 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6661 Ykey_6_eat, FALSE, FALSE,
6662 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6665 Ykey_7_eat, FALSE, FALSE,
6666 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6669 Ykey_8_eat, FALSE, FALSE,
6670 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6673 Ylenses_eat, FALSE, FALSE,
6674 EL_EMC_LENSES, ACTION_COLLECTING, -1
6677 Ymagnify_eat, FALSE, FALSE,
6678 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6681 Ygrass_eat, FALSE, FALSE,
6682 EL_EMC_GRASS, ACTION_SNAPPING, -1
6685 Ydirt_eat, FALSE, FALSE,
6686 EL_SAND, ACTION_SNAPPING, -1
6689 Xgrow_ns, TRUE, FALSE,
6690 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6693 Ygrow_ns_eat, FALSE, FALSE,
6694 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6697 Xgrow_ew, TRUE, FALSE,
6698 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6701 Ygrow_ew_eat, FALSE, FALSE,
6702 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6705 Xwonderwall, TRUE, FALSE,
6706 EL_MAGIC_WALL, -1, -1
6709 XwonderwallB, FALSE, FALSE,
6710 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6713 Xamoeba_1, TRUE, FALSE,
6714 EL_AMOEBA_DRY, ACTION_OTHER, -1
6717 Xamoeba_2, FALSE, FALSE,
6718 EL_AMOEBA_DRY, ACTION_OTHER, -1
6721 Xamoeba_3, FALSE, FALSE,
6722 EL_AMOEBA_DRY, ACTION_OTHER, -1
6725 Xamoeba_4, FALSE, FALSE,
6726 EL_AMOEBA_DRY, ACTION_OTHER, -1
6729 Xamoeba_5, TRUE, FALSE,
6730 EL_AMOEBA_WET, ACTION_OTHER, -1
6733 Xamoeba_6, FALSE, FALSE,
6734 EL_AMOEBA_WET, ACTION_OTHER, -1
6737 Xamoeba_7, FALSE, FALSE,
6738 EL_AMOEBA_WET, ACTION_OTHER, -1
6741 Xamoeba_8, FALSE, FALSE,
6742 EL_AMOEBA_WET, ACTION_OTHER, -1
6745 Xdoor_1, TRUE, FALSE,
6746 EL_EM_GATE_1, -1, -1
6749 Xdoor_2, TRUE, FALSE,
6750 EL_EM_GATE_2, -1, -1
6753 Xdoor_3, TRUE, FALSE,
6754 EL_EM_GATE_3, -1, -1
6757 Xdoor_4, TRUE, FALSE,
6758 EL_EM_GATE_4, -1, -1
6761 Xdoor_5, TRUE, FALSE,
6762 EL_EMC_GATE_5, -1, -1
6765 Xdoor_6, TRUE, FALSE,
6766 EL_EMC_GATE_6, -1, -1
6769 Xdoor_7, TRUE, FALSE,
6770 EL_EMC_GATE_7, -1, -1
6773 Xdoor_8, TRUE, FALSE,
6774 EL_EMC_GATE_8, -1, -1
6777 Xkey_1, TRUE, FALSE,
6781 Xkey_2, TRUE, FALSE,
6785 Xkey_3, TRUE, FALSE,
6789 Xkey_4, TRUE, FALSE,
6793 Xkey_5, TRUE, FALSE,
6794 EL_EMC_KEY_5, -1, -1
6797 Xkey_6, TRUE, FALSE,
6798 EL_EMC_KEY_6, -1, -1
6801 Xkey_7, TRUE, FALSE,
6802 EL_EMC_KEY_7, -1, -1
6805 Xkey_8, TRUE, FALSE,
6806 EL_EMC_KEY_8, -1, -1
6809 Xwind_n, TRUE, FALSE,
6810 EL_BALLOON_SWITCH_UP, -1, -1
6813 Xwind_e, TRUE, FALSE,
6814 EL_BALLOON_SWITCH_RIGHT, -1, -1
6817 Xwind_s, TRUE, FALSE,
6818 EL_BALLOON_SWITCH_DOWN, -1, -1
6821 Xwind_w, TRUE, FALSE,
6822 EL_BALLOON_SWITCH_LEFT, -1, -1
6825 Xwind_nesw, TRUE, FALSE,
6826 EL_BALLOON_SWITCH_ANY, -1, -1
6829 Xwind_stop, TRUE, FALSE,
6830 EL_BALLOON_SWITCH_NONE, -1, -1
6834 EL_EM_EXIT_CLOSED, -1, -1
6837 Xexit_1, TRUE, FALSE,
6838 EL_EM_EXIT_OPEN, -1, -1
6841 Xexit_2, FALSE, FALSE,
6842 EL_EM_EXIT_OPEN, -1, -1
6845 Xexit_3, FALSE, FALSE,
6846 EL_EM_EXIT_OPEN, -1, -1
6849 Xdynamite, TRUE, FALSE,
6850 EL_EM_DYNAMITE, -1, -1
6853 Ydynamite_eat, FALSE, FALSE,
6854 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6857 Xdynamite_1, TRUE, FALSE,
6858 EL_EM_DYNAMITE_ACTIVE, -1, -1
6861 Xdynamite_2, FALSE, FALSE,
6862 EL_EM_DYNAMITE_ACTIVE, -1, -1
6865 Xdynamite_3, FALSE, FALSE,
6866 EL_EM_DYNAMITE_ACTIVE, -1, -1
6869 Xdynamite_4, FALSE, FALSE,
6870 EL_EM_DYNAMITE_ACTIVE, -1, -1
6873 Xbumper, TRUE, FALSE,
6874 EL_EMC_SPRING_BUMPER, -1, -1
6877 XbumperB, FALSE, FALSE,
6878 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6881 Xwheel, TRUE, FALSE,
6882 EL_ROBOT_WHEEL, -1, -1
6885 XwheelB, FALSE, FALSE,
6886 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6889 Xswitch, TRUE, FALSE,
6890 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6893 XswitchB, FALSE, FALSE,
6894 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6898 EL_QUICKSAND_EMPTY, -1, -1
6901 Xsand_stone, TRUE, FALSE,
6902 EL_QUICKSAND_FULL, -1, -1
6905 Xsand_stonein_1, FALSE, TRUE,
6906 EL_ROCK, ACTION_FILLING, -1
6909 Xsand_stonein_2, FALSE, TRUE,
6910 EL_ROCK, ACTION_FILLING, -1
6913 Xsand_stonein_3, FALSE, TRUE,
6914 EL_ROCK, ACTION_FILLING, -1
6917 Xsand_stonein_4, FALSE, TRUE,
6918 EL_ROCK, ACTION_FILLING, -1
6921 Xsand_stonesand_1, FALSE, FALSE,
6922 EL_QUICKSAND_EMPTYING, -1, -1
6925 Xsand_stonesand_2, FALSE, FALSE,
6926 EL_QUICKSAND_EMPTYING, -1, -1
6929 Xsand_stonesand_3, FALSE, FALSE,
6930 EL_QUICKSAND_EMPTYING, -1, -1
6933 Xsand_stonesand_4, FALSE, FALSE,
6934 EL_QUICKSAND_EMPTYING, -1, -1
6937 Xsand_stonesand_quickout_1, FALSE, FALSE,
6938 EL_QUICKSAND_EMPTYING, -1, -1
6941 Xsand_stonesand_quickout_2, FALSE, FALSE,
6942 EL_QUICKSAND_EMPTYING, -1, -1
6945 Xsand_stoneout_1, FALSE, FALSE,
6946 EL_ROCK, ACTION_EMPTYING, -1
6949 Xsand_stoneout_2, FALSE, FALSE,
6950 EL_ROCK, ACTION_EMPTYING, -1
6953 Xsand_sandstone_1, FALSE, FALSE,
6954 EL_QUICKSAND_FILLING, -1, -1
6957 Xsand_sandstone_2, FALSE, FALSE,
6958 EL_QUICKSAND_FILLING, -1, -1
6961 Xsand_sandstone_3, FALSE, FALSE,
6962 EL_QUICKSAND_FILLING, -1, -1
6965 Xsand_sandstone_4, FALSE, FALSE,
6966 EL_QUICKSAND_FILLING, -1, -1
6969 Xplant, TRUE, FALSE,
6970 EL_EMC_PLANT, -1, -1
6973 Yplant, FALSE, FALSE,
6974 EL_EMC_PLANT, -1, -1
6977 Xlenses, TRUE, FALSE,
6978 EL_EMC_LENSES, -1, -1
6981 Xmagnify, TRUE, FALSE,
6982 EL_EMC_MAGNIFIER, -1, -1
6985 Xdripper, TRUE, FALSE,
6986 EL_EMC_DRIPPER, -1, -1
6989 XdripperB, FALSE, FALSE,
6990 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6993 Xfake_blank, TRUE, FALSE,
6994 EL_INVISIBLE_WALL, -1, -1
6997 Xfake_blankB, FALSE, FALSE,
6998 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
7001 Xfake_grass, TRUE, FALSE,
7002 EL_EMC_FAKE_GRASS, -1, -1
7005 Xfake_grassB, FALSE, FALSE,
7006 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
7009 Xfake_door_1, TRUE, FALSE,
7010 EL_EM_GATE_1_GRAY, -1, -1
7013 Xfake_door_2, TRUE, FALSE,
7014 EL_EM_GATE_2_GRAY, -1, -1
7017 Xfake_door_3, TRUE, FALSE,
7018 EL_EM_GATE_3_GRAY, -1, -1
7021 Xfake_door_4, TRUE, FALSE,
7022 EL_EM_GATE_4_GRAY, -1, -1
7025 Xfake_door_5, TRUE, FALSE,
7026 EL_EMC_GATE_5_GRAY, -1, -1
7029 Xfake_door_6, TRUE, FALSE,
7030 EL_EMC_GATE_6_GRAY, -1, -1
7033 Xfake_door_7, TRUE, FALSE,
7034 EL_EMC_GATE_7_GRAY, -1, -1
7037 Xfake_door_8, TRUE, FALSE,
7038 EL_EMC_GATE_8_GRAY, -1, -1
7041 Xfake_acid_1, TRUE, FALSE,
7042 EL_EMC_FAKE_ACID, -1, -1
7045 Xfake_acid_2, FALSE, FALSE,
7046 EL_EMC_FAKE_ACID, -1, -1
7049 Xfake_acid_3, FALSE, FALSE,
7050 EL_EMC_FAKE_ACID, -1, -1
7053 Xfake_acid_4, FALSE, FALSE,
7054 EL_EMC_FAKE_ACID, -1, -1
7057 Xfake_acid_5, FALSE, FALSE,
7058 EL_EMC_FAKE_ACID, -1, -1
7061 Xfake_acid_6, FALSE, FALSE,
7062 EL_EMC_FAKE_ACID, -1, -1
7065 Xfake_acid_7, FALSE, FALSE,
7066 EL_EMC_FAKE_ACID, -1, -1
7069 Xfake_acid_8, FALSE, FALSE,
7070 EL_EMC_FAKE_ACID, -1, -1
7073 Xsteel_1, TRUE, FALSE,
7074 EL_STEELWALL, -1, -1
7077 Xsteel_2, TRUE, FALSE,
7078 EL_EMC_STEELWALL_2, -1, -1
7081 Xsteel_3, TRUE, FALSE,
7082 EL_EMC_STEELWALL_3, -1, -1
7085 Xsteel_4, TRUE, FALSE,
7086 EL_EMC_STEELWALL_4, -1, -1
7089 Xwall_1, TRUE, FALSE,
7093 Xwall_2, TRUE, FALSE,
7094 EL_EMC_WALL_14, -1, -1
7097 Xwall_3, TRUE, FALSE,
7098 EL_EMC_WALL_15, -1, -1
7101 Xwall_4, TRUE, FALSE,
7102 EL_EMC_WALL_16, -1, -1
7105 Xround_wall_1, TRUE, FALSE,
7106 EL_WALL_SLIPPERY, -1, -1
7109 Xround_wall_2, TRUE, FALSE,
7110 EL_EMC_WALL_SLIPPERY_2, -1, -1
7113 Xround_wall_3, TRUE, FALSE,
7114 EL_EMC_WALL_SLIPPERY_3, -1, -1
7117 Xround_wall_4, TRUE, FALSE,
7118 EL_EMC_WALL_SLIPPERY_4, -1, -1
7121 Xdecor_1, TRUE, FALSE,
7122 EL_EMC_WALL_8, -1, -1
7125 Xdecor_2, TRUE, FALSE,
7126 EL_EMC_WALL_6, -1, -1
7129 Xdecor_3, TRUE, FALSE,
7130 EL_EMC_WALL_4, -1, -1
7133 Xdecor_4, TRUE, FALSE,
7134 EL_EMC_WALL_7, -1, -1
7137 Xdecor_5, TRUE, FALSE,
7138 EL_EMC_WALL_5, -1, -1
7141 Xdecor_6, TRUE, FALSE,
7142 EL_EMC_WALL_9, -1, -1
7145 Xdecor_7, TRUE, FALSE,
7146 EL_EMC_WALL_10, -1, -1
7149 Xdecor_8, TRUE, FALSE,
7150 EL_EMC_WALL_1, -1, -1
7153 Xdecor_9, TRUE, FALSE,
7154 EL_EMC_WALL_2, -1, -1
7157 Xdecor_10, TRUE, FALSE,
7158 EL_EMC_WALL_3, -1, -1
7161 Xdecor_11, TRUE, FALSE,
7162 EL_EMC_WALL_11, -1, -1
7165 Xdecor_12, TRUE, FALSE,
7166 EL_EMC_WALL_12, -1, -1
7169 Xalpha_0, TRUE, FALSE,
7170 EL_CHAR('0'), -1, -1
7173 Xalpha_1, TRUE, FALSE,
7174 EL_CHAR('1'), -1, -1
7177 Xalpha_2, TRUE, FALSE,
7178 EL_CHAR('2'), -1, -1
7181 Xalpha_3, TRUE, FALSE,
7182 EL_CHAR('3'), -1, -1
7185 Xalpha_4, TRUE, FALSE,
7186 EL_CHAR('4'), -1, -1
7189 Xalpha_5, TRUE, FALSE,
7190 EL_CHAR('5'), -1, -1
7193 Xalpha_6, TRUE, FALSE,
7194 EL_CHAR('6'), -1, -1
7197 Xalpha_7, TRUE, FALSE,
7198 EL_CHAR('7'), -1, -1
7201 Xalpha_8, TRUE, FALSE,
7202 EL_CHAR('8'), -1, -1
7205 Xalpha_9, TRUE, FALSE,
7206 EL_CHAR('9'), -1, -1
7209 Xalpha_excla, TRUE, FALSE,
7210 EL_CHAR('!'), -1, -1
7213 Xalpha_quote, TRUE, FALSE,
7214 EL_CHAR('"'), -1, -1
7217 Xalpha_comma, TRUE, FALSE,
7218 EL_CHAR(','), -1, -1
7221 Xalpha_minus, TRUE, FALSE,
7222 EL_CHAR('-'), -1, -1
7225 Xalpha_perio, TRUE, FALSE,
7226 EL_CHAR('.'), -1, -1
7229 Xalpha_colon, TRUE, FALSE,
7230 EL_CHAR(':'), -1, -1
7233 Xalpha_quest, TRUE, FALSE,
7234 EL_CHAR('?'), -1, -1
7237 Xalpha_a, TRUE, FALSE,
7238 EL_CHAR('A'), -1, -1
7241 Xalpha_b, TRUE, FALSE,
7242 EL_CHAR('B'), -1, -1
7245 Xalpha_c, TRUE, FALSE,
7246 EL_CHAR('C'), -1, -1
7249 Xalpha_d, TRUE, FALSE,
7250 EL_CHAR('D'), -1, -1
7253 Xalpha_e, TRUE, FALSE,
7254 EL_CHAR('E'), -1, -1
7257 Xalpha_f, TRUE, FALSE,
7258 EL_CHAR('F'), -1, -1
7261 Xalpha_g, TRUE, FALSE,
7262 EL_CHAR('G'), -1, -1
7265 Xalpha_h, TRUE, FALSE,
7266 EL_CHAR('H'), -1, -1
7269 Xalpha_i, TRUE, FALSE,
7270 EL_CHAR('I'), -1, -1
7273 Xalpha_j, TRUE, FALSE,
7274 EL_CHAR('J'), -1, -1
7277 Xalpha_k, TRUE, FALSE,
7278 EL_CHAR('K'), -1, -1
7281 Xalpha_l, TRUE, FALSE,
7282 EL_CHAR('L'), -1, -1
7285 Xalpha_m, TRUE, FALSE,
7286 EL_CHAR('M'), -1, -1
7289 Xalpha_n, TRUE, FALSE,
7290 EL_CHAR('N'), -1, -1
7293 Xalpha_o, TRUE, FALSE,
7294 EL_CHAR('O'), -1, -1
7297 Xalpha_p, TRUE, FALSE,
7298 EL_CHAR('P'), -1, -1
7301 Xalpha_q, TRUE, FALSE,
7302 EL_CHAR('Q'), -1, -1
7305 Xalpha_r, TRUE, FALSE,
7306 EL_CHAR('R'), -1, -1
7309 Xalpha_s, TRUE, FALSE,
7310 EL_CHAR('S'), -1, -1
7313 Xalpha_t, TRUE, FALSE,
7314 EL_CHAR('T'), -1, -1
7317 Xalpha_u, TRUE, FALSE,
7318 EL_CHAR('U'), -1, -1
7321 Xalpha_v, TRUE, FALSE,
7322 EL_CHAR('V'), -1, -1
7325 Xalpha_w, TRUE, FALSE,
7326 EL_CHAR('W'), -1, -1
7329 Xalpha_x, TRUE, FALSE,
7330 EL_CHAR('X'), -1, -1
7333 Xalpha_y, TRUE, FALSE,
7334 EL_CHAR('Y'), -1, -1
7337 Xalpha_z, TRUE, FALSE,
7338 EL_CHAR('Z'), -1, -1
7341 Xalpha_arrow_e, TRUE, FALSE,
7342 EL_CHAR('>'), -1, -1
7345 Xalpha_arrow_w, TRUE, FALSE,
7346 EL_CHAR('<'), -1, -1
7349 Xalpha_copyr, TRUE, FALSE,
7350 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7354 Xboom_bug, FALSE, FALSE,
7355 EL_BUG, ACTION_EXPLODING, -1
7358 Xboom_bomb, FALSE, FALSE,
7359 EL_BOMB, ACTION_EXPLODING, -1
7362 Xboom_android, FALSE, FALSE,
7363 EL_EMC_ANDROID, ACTION_OTHER, -1
7366 Xboom_1, FALSE, FALSE,
7367 EL_DEFAULT, ACTION_EXPLODING, -1
7370 Xboom_2, FALSE, FALSE,
7371 EL_DEFAULT, ACTION_EXPLODING, -1
7374 Znormal, FALSE, FALSE,
7378 Zdynamite, FALSE, FALSE,
7382 Zplayer, FALSE, FALSE,
7386 ZBORDER, FALSE, FALSE,
7396 static struct Mapping_EM_to_RND_player
7405 em_player_mapping_list[] =
7409 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7413 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7417 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7421 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7425 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7429 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7433 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7437 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7441 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7445 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7449 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7453 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7457 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7461 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7465 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7469 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7473 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7477 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7481 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7485 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7489 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7493 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7497 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7501 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7505 EL_PLAYER_1, ACTION_DEFAULT, -1,
7509 EL_PLAYER_2, ACTION_DEFAULT, -1,
7513 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7517 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7521 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7525 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7529 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7533 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7537 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7541 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7545 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7549 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7553 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7557 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7561 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7565 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7569 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7573 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7577 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7581 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7585 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7589 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7593 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7597 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7601 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7605 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7609 EL_PLAYER_3, ACTION_DEFAULT, -1,
7613 EL_PLAYER_4, ACTION_DEFAULT, -1,
7622 int map_element_RND_to_EM(int element_rnd)
7624 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7625 static boolean mapping_initialized = FALSE;
7627 if (!mapping_initialized)
7631 // return "Xalpha_quest" for all undefined elements in mapping array
7632 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7633 mapping_RND_to_EM[i] = Xalpha_quest;
7635 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7636 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7637 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7638 em_object_mapping_list[i].element_em;
7640 mapping_initialized = TRUE;
7643 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7644 return mapping_RND_to_EM[element_rnd];
7646 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7651 int map_element_EM_to_RND(int element_em)
7653 static unsigned short mapping_EM_to_RND[TILE_MAX];
7654 static boolean mapping_initialized = FALSE;
7656 if (!mapping_initialized)
7660 // return "EL_UNKNOWN" for all undefined elements in mapping array
7661 for (i = 0; i < TILE_MAX; i++)
7662 mapping_EM_to_RND[i] = EL_UNKNOWN;
7664 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7665 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7666 em_object_mapping_list[i].element_rnd;
7668 mapping_initialized = TRUE;
7671 if (element_em >= 0 && element_em < TILE_MAX)
7672 return mapping_EM_to_RND[element_em];
7674 Error(ERR_WARN, "invalid EM level element %d", element_em);
7679 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7681 struct LevelInfo_EM *level_em = level->native_em_level;
7682 struct LEVEL *lev = level_em->lev;
7685 for (i = 0; i < TILE_MAX; i++)
7686 lev->android_array[i] = Xblank;
7688 for (i = 0; i < level->num_android_clone_elements; i++)
7690 int element_rnd = level->android_clone_element[i];
7691 int element_em = map_element_RND_to_EM(element_rnd);
7693 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7694 if (em_object_mapping_list[j].element_rnd == element_rnd)
7695 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7699 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7701 struct LevelInfo_EM *level_em = level->native_em_level;
7702 struct LEVEL *lev = level_em->lev;
7705 level->num_android_clone_elements = 0;
7707 for (i = 0; i < TILE_MAX; i++)
7709 int element_em = lev->android_array[i];
7711 boolean element_found = FALSE;
7713 if (element_em == Xblank)
7716 element_rnd = map_element_EM_to_RND(element_em);
7718 for (j = 0; j < level->num_android_clone_elements; j++)
7719 if (level->android_clone_element[j] == element_rnd)
7720 element_found = TRUE;
7724 level->android_clone_element[level->num_android_clone_elements++] =
7727 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7732 if (level->num_android_clone_elements == 0)
7734 level->num_android_clone_elements = 1;
7735 level->android_clone_element[0] = EL_EMPTY;
7739 int map_direction_RND_to_EM(int direction)
7741 return (direction == MV_UP ? 0 :
7742 direction == MV_RIGHT ? 1 :
7743 direction == MV_DOWN ? 2 :
7744 direction == MV_LEFT ? 3 :
7748 int map_direction_EM_to_RND(int direction)
7750 return (direction == 0 ? MV_UP :
7751 direction == 1 ? MV_RIGHT :
7752 direction == 2 ? MV_DOWN :
7753 direction == 3 ? MV_LEFT :
7757 int map_element_RND_to_SP(int element_rnd)
7759 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7761 if (element_rnd >= EL_SP_START &&
7762 element_rnd <= EL_SP_END)
7763 element_sp = element_rnd - EL_SP_START;
7764 else if (element_rnd == EL_EMPTY_SPACE)
7766 else if (element_rnd == EL_INVISIBLE_WALL)
7772 int map_element_SP_to_RND(int element_sp)
7774 int element_rnd = EL_UNKNOWN;
7776 if (element_sp >= 0x00 &&
7778 element_rnd = EL_SP_START + element_sp;
7779 else if (element_sp == 0x28)
7780 element_rnd = EL_INVISIBLE_WALL;
7785 int map_action_SP_to_RND(int action_sp)
7789 case actActive: return ACTION_ACTIVE;
7790 case actImpact: return ACTION_IMPACT;
7791 case actExploding: return ACTION_EXPLODING;
7792 case actDigging: return ACTION_DIGGING;
7793 case actSnapping: return ACTION_SNAPPING;
7794 case actCollecting: return ACTION_COLLECTING;
7795 case actPassing: return ACTION_PASSING;
7796 case actPushing: return ACTION_PUSHING;
7797 case actDropping: return ACTION_DROPPING;
7799 default: return ACTION_DEFAULT;
7803 int map_element_RND_to_MM(int element_rnd)
7805 return (element_rnd >= EL_MM_START_1 &&
7806 element_rnd <= EL_MM_END_1 ?
7807 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7809 element_rnd >= EL_MM_START_2 &&
7810 element_rnd <= EL_MM_END_2 ?
7811 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7813 element_rnd >= EL_CHAR_START &&
7814 element_rnd <= EL_CHAR_END ?
7815 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7817 element_rnd >= EL_MM_RUNTIME_START &&
7818 element_rnd <= EL_MM_RUNTIME_END ?
7819 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7821 element_rnd >= EL_MM_DUMMY_START &&
7822 element_rnd <= EL_MM_DUMMY_END ?
7823 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7825 EL_MM_EMPTY_NATIVE);
7828 int map_element_MM_to_RND(int element_mm)
7830 return (element_mm == EL_MM_EMPTY_NATIVE ||
7831 element_mm == EL_DF_EMPTY_NATIVE ?
7834 element_mm >= EL_MM_START_1_NATIVE &&
7835 element_mm <= EL_MM_END_1_NATIVE ?
7836 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7838 element_mm >= EL_MM_START_2_NATIVE &&
7839 element_mm <= EL_MM_END_2_NATIVE ?
7840 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7842 element_mm >= EL_MM_CHAR_START_NATIVE &&
7843 element_mm <= EL_MM_CHAR_END_NATIVE ?
7844 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7846 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7847 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7848 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7850 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7851 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7852 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7857 int map_action_MM_to_RND(int action_mm)
7859 // all MM actions are defined to exactly match their RND counterparts
7863 int map_sound_MM_to_RND(int sound_mm)
7867 case SND_MM_GAME_LEVELTIME_CHARGING:
7868 return SND_GAME_LEVELTIME_CHARGING;
7870 case SND_MM_GAME_HEALTH_CHARGING:
7871 return SND_GAME_HEALTH_CHARGING;
7874 return SND_UNDEFINED;
7878 int map_mm_wall_element(int element)
7880 return (element >= EL_MM_STEEL_WALL_START &&
7881 element <= EL_MM_STEEL_WALL_END ?
7884 element >= EL_MM_WOODEN_WALL_START &&
7885 element <= EL_MM_WOODEN_WALL_END ?
7888 element >= EL_MM_ICE_WALL_START &&
7889 element <= EL_MM_ICE_WALL_END ?
7892 element >= EL_MM_AMOEBA_WALL_START &&
7893 element <= EL_MM_AMOEBA_WALL_END ?
7896 element >= EL_DF_STEEL_WALL_START &&
7897 element <= EL_DF_STEEL_WALL_END ?
7900 element >= EL_DF_WOODEN_WALL_START &&
7901 element <= EL_DF_WOODEN_WALL_END ?
7907 int map_mm_wall_element_editor(int element)
7911 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7912 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7913 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7914 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7915 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7916 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7918 default: return element;
7922 int get_next_element(int element)
7926 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7927 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7928 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7929 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7930 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7931 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7932 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7933 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7934 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7935 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7936 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7938 default: return element;
7942 int el2img_mm(int element_mm)
7944 return el2img(map_element_MM_to_RND(element_mm));
7947 int el_act_dir2img(int element, int action, int direction)
7949 element = GFX_ELEMENT(element);
7950 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7952 // direction_graphic[][] == graphic[] for undefined direction graphics
7953 return element_info[element].direction_graphic[action][direction];
7956 static int el_act_dir2crm(int element, int action, int direction)
7958 element = GFX_ELEMENT(element);
7959 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7961 // direction_graphic[][] == graphic[] for undefined direction graphics
7962 return element_info[element].direction_crumbled[action][direction];
7965 int el_act2img(int element, int action)
7967 element = GFX_ELEMENT(element);
7969 return element_info[element].graphic[action];
7972 int el_act2crm(int element, int action)
7974 element = GFX_ELEMENT(element);
7976 return element_info[element].crumbled[action];
7979 int el_dir2img(int element, int direction)
7981 element = GFX_ELEMENT(element);
7983 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7986 int el2baseimg(int element)
7988 return element_info[element].graphic[ACTION_DEFAULT];
7991 int el2img(int element)
7993 element = GFX_ELEMENT(element);
7995 return element_info[element].graphic[ACTION_DEFAULT];
7998 int el2edimg(int element)
8000 element = GFX_ELEMENT(element);
8002 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
8005 int el2preimg(int element)
8007 element = GFX_ELEMENT(element);
8009 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8012 int el2panelimg(int element)
8014 element = GFX_ELEMENT(element);
8016 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8019 int font2baseimg(int font_nr)
8021 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8024 int getBeltNrFromBeltElement(int element)
8026 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8027 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8028 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8031 int getBeltNrFromBeltActiveElement(int element)
8033 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8034 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8035 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8038 int getBeltNrFromBeltSwitchElement(int element)
8040 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8041 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8042 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8045 int getBeltDirNrFromBeltElement(int element)
8047 static int belt_base_element[4] =
8049 EL_CONVEYOR_BELT_1_LEFT,
8050 EL_CONVEYOR_BELT_2_LEFT,
8051 EL_CONVEYOR_BELT_3_LEFT,
8052 EL_CONVEYOR_BELT_4_LEFT
8055 int belt_nr = getBeltNrFromBeltElement(element);
8056 int belt_dir_nr = element - belt_base_element[belt_nr];
8058 return (belt_dir_nr % 3);
8061 int getBeltDirNrFromBeltSwitchElement(int element)
8063 static int belt_base_element[4] =
8065 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8066 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8067 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8068 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8071 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8072 int belt_dir_nr = element - belt_base_element[belt_nr];
8074 return (belt_dir_nr % 3);
8077 int getBeltDirFromBeltElement(int element)
8079 static int belt_move_dir[3] =
8086 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8088 return belt_move_dir[belt_dir_nr];
8091 int getBeltDirFromBeltSwitchElement(int element)
8093 static int belt_move_dir[3] =
8100 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8102 return belt_move_dir[belt_dir_nr];
8105 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8107 static int belt_base_element[4] =
8109 EL_CONVEYOR_BELT_1_LEFT,
8110 EL_CONVEYOR_BELT_2_LEFT,
8111 EL_CONVEYOR_BELT_3_LEFT,
8112 EL_CONVEYOR_BELT_4_LEFT
8115 return belt_base_element[belt_nr] + belt_dir_nr;
8118 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8120 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8122 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8125 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8127 static int belt_base_element[4] =
8129 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8130 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8131 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8132 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8135 return belt_base_element[belt_nr] + belt_dir_nr;
8138 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8140 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8142 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8145 boolean getTeamMode_EM(void)
8147 return game.team_mode || network_playing;
8150 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8152 int game_frame_delay_value;
8154 game_frame_delay_value =
8155 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8156 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8159 if (tape.playing && tape.warp_forward && !tape.pausing)
8160 game_frame_delay_value = 0;
8162 return game_frame_delay_value;
8165 unsigned int InitRND(int seed)
8167 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8168 return InitEngineRandom_EM(seed);
8169 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8170 return InitEngineRandom_SP(seed);
8171 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8172 return InitEngineRandom_MM(seed);
8174 return InitEngineRandom_RND(seed);
8177 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8178 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8180 static int get_effective_element_EM(int tile, int frame_em)
8182 int element = object_mapping[tile].element_rnd;
8183 int action = object_mapping[tile].action;
8184 boolean is_backside = object_mapping[tile].is_backside;
8185 boolean action_removing = (action == ACTION_DIGGING ||
8186 action == ACTION_SNAPPING ||
8187 action == ACTION_COLLECTING);
8193 case Yacid_splash_eB:
8194 case Yacid_splash_wB:
8195 return (frame_em > 5 ? EL_EMPTY : element);
8201 else // frame_em == 7
8205 case Yacid_splash_eB:
8206 case Yacid_splash_wB:
8209 case Yemerald_stone:
8212 case Ydiamond_stone:
8216 case Xdrip_stretchB:
8235 case Xsand_stonein_1:
8236 case Xsand_stonein_2:
8237 case Xsand_stonein_3:
8238 case Xsand_stonein_4:
8242 return (is_backside || action_removing ? EL_EMPTY : element);
8247 static boolean check_linear_animation_EM(int tile)
8251 case Xsand_stonesand_1:
8252 case Xsand_stonesand_quickout_1:
8253 case Xsand_sandstone_1:
8254 case Xsand_stonein_1:
8255 case Xsand_stoneout_1:
8274 case Yacid_splash_eB:
8275 case Yacid_splash_wB:
8276 case Yemerald_stone:
8283 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8284 boolean has_crumbled_graphics,
8285 int crumbled, int sync_frame)
8287 // if element can be crumbled, but certain action graphics are just empty
8288 // space (like instantly snapping sand to empty space in 1 frame), do not
8289 // treat these empty space graphics as crumbled graphics in EMC engine
8290 if (crumbled == IMG_EMPTY_SPACE)
8291 has_crumbled_graphics = FALSE;
8293 if (has_crumbled_graphics)
8295 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8296 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8297 g_crumbled->anim_delay,
8298 g_crumbled->anim_mode,
8299 g_crumbled->anim_start_frame,
8302 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8303 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8305 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8306 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8308 g_em->has_crumbled_graphics = TRUE;
8312 g_em->crumbled_bitmap = NULL;
8313 g_em->crumbled_src_x = 0;
8314 g_em->crumbled_src_y = 0;
8315 g_em->crumbled_border_size = 0;
8316 g_em->crumbled_tile_size = 0;
8318 g_em->has_crumbled_graphics = FALSE;
8323 void ResetGfxAnimation_EM(int x, int y, int tile)
8329 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8330 int tile, int frame_em, int x, int y)
8332 int action = object_mapping[tile].action;
8333 int direction = object_mapping[tile].direction;
8334 int effective_element = get_effective_element_EM(tile, frame_em);
8335 int graphic = (direction == MV_NONE ?
8336 el_act2img(effective_element, action) :
8337 el_act_dir2img(effective_element, action, direction));
8338 struct GraphicInfo *g = &graphic_info[graphic];
8340 boolean action_removing = (action == ACTION_DIGGING ||
8341 action == ACTION_SNAPPING ||
8342 action == ACTION_COLLECTING);
8343 boolean action_moving = (action == ACTION_FALLING ||
8344 action == ACTION_MOVING ||
8345 action == ACTION_PUSHING ||
8346 action == ACTION_EATING ||
8347 action == ACTION_FILLING ||
8348 action == ACTION_EMPTYING);
8349 boolean action_falling = (action == ACTION_FALLING ||
8350 action == ACTION_FILLING ||
8351 action == ACTION_EMPTYING);
8353 // special case: graphic uses "2nd movement tile" and has defined
8354 // 7 frames for movement animation (or less) => use default graphic
8355 // for last (8th) frame which ends the movement animation
8356 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8358 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8359 graphic = (direction == MV_NONE ?
8360 el_act2img(effective_element, action) :
8361 el_act_dir2img(effective_element, action, direction));
8363 g = &graphic_info[graphic];
8366 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8370 else if (action_moving)
8372 boolean is_backside = object_mapping[tile].is_backside;
8376 int direction = object_mapping[tile].direction;
8377 int move_dir = (action_falling ? MV_DOWN : direction);
8382 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8383 if (g->double_movement && frame_em == 0)
8387 if (move_dir == MV_LEFT)
8388 GfxFrame[x - 1][y] = GfxFrame[x][y];
8389 else if (move_dir == MV_RIGHT)
8390 GfxFrame[x + 1][y] = GfxFrame[x][y];
8391 else if (move_dir == MV_UP)
8392 GfxFrame[x][y - 1] = GfxFrame[x][y];
8393 else if (move_dir == MV_DOWN)
8394 GfxFrame[x][y + 1] = GfxFrame[x][y];
8401 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8402 if (tile == Xsand_stonesand_quickout_1 ||
8403 tile == Xsand_stonesand_quickout_2)
8407 if (graphic_info[graphic].anim_global_sync)
8408 sync_frame = FrameCounter;
8409 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8410 sync_frame = GfxFrame[x][y];
8412 sync_frame = 0; // playfield border (pseudo steel)
8414 SetRandomAnimationValue(x, y);
8416 int frame = getAnimationFrame(g->anim_frames,
8419 g->anim_start_frame,
8422 g_em->unique_identifier =
8423 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8426 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8427 int tile, int frame_em, int x, int y)
8429 int action = object_mapping[tile].action;
8430 int direction = object_mapping[tile].direction;
8431 boolean is_backside = object_mapping[tile].is_backside;
8432 int effective_element = get_effective_element_EM(tile, frame_em);
8433 int effective_action = action;
8434 int graphic = (direction == MV_NONE ?
8435 el_act2img(effective_element, effective_action) :
8436 el_act_dir2img(effective_element, effective_action,
8438 int crumbled = (direction == MV_NONE ?
8439 el_act2crm(effective_element, effective_action) :
8440 el_act_dir2crm(effective_element, effective_action,
8442 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8443 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8444 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8445 struct GraphicInfo *g = &graphic_info[graphic];
8448 // special case: graphic uses "2nd movement tile" and has defined
8449 // 7 frames for movement animation (or less) => use default graphic
8450 // for last (8th) frame which ends the movement animation
8451 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8453 effective_action = ACTION_DEFAULT;
8454 graphic = (direction == MV_NONE ?
8455 el_act2img(effective_element, effective_action) :
8456 el_act_dir2img(effective_element, effective_action,
8458 crumbled = (direction == MV_NONE ?
8459 el_act2crm(effective_element, effective_action) :
8460 el_act_dir2crm(effective_element, effective_action,
8463 g = &graphic_info[graphic];
8466 if (graphic_info[graphic].anim_global_sync)
8467 sync_frame = FrameCounter;
8468 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8469 sync_frame = GfxFrame[x][y];
8471 sync_frame = 0; // playfield border (pseudo steel)
8473 SetRandomAnimationValue(x, y);
8475 int frame = getAnimationFrame(g->anim_frames,
8478 g->anim_start_frame,
8481 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8482 g->double_movement && is_backside);
8484 // (updating the "crumbled" graphic definitions is probably not really needed,
8485 // as animations for crumbled graphics can't be longer than one EMC cycle)
8486 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8490 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8491 int player_nr, int anim, int frame_em)
8493 int element = player_mapping[player_nr][anim].element_rnd;
8494 int action = player_mapping[player_nr][anim].action;
8495 int direction = player_mapping[player_nr][anim].direction;
8496 int graphic = (direction == MV_NONE ?
8497 el_act2img(element, action) :
8498 el_act_dir2img(element, action, direction));
8499 struct GraphicInfo *g = &graphic_info[graphic];
8502 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8504 stored_player[player_nr].StepFrame = frame_em;
8506 sync_frame = stored_player[player_nr].Frame;
8508 int frame = getAnimationFrame(g->anim_frames,
8511 g->anim_start_frame,
8514 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8515 &g_em->src_x, &g_em->src_y, FALSE);
8518 void InitGraphicInfo_EM(void)
8523 int num_em_gfx_errors = 0;
8525 if (graphic_info_em_object[0][0].bitmap == NULL)
8527 // EM graphics not yet initialized in em_open_all()
8532 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8535 // always start with reliable default values
8536 for (i = 0; i < TILE_MAX; i++)
8538 object_mapping[i].element_rnd = EL_UNKNOWN;
8539 object_mapping[i].is_backside = FALSE;
8540 object_mapping[i].action = ACTION_DEFAULT;
8541 object_mapping[i].direction = MV_NONE;
8544 // always start with reliable default values
8545 for (p = 0; p < MAX_PLAYERS; p++)
8547 for (i = 0; i < SPR_MAX; i++)
8549 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8550 player_mapping[p][i].action = ACTION_DEFAULT;
8551 player_mapping[p][i].direction = MV_NONE;
8555 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8557 int e = em_object_mapping_list[i].element_em;
8559 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8560 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8562 if (em_object_mapping_list[i].action != -1)
8563 object_mapping[e].action = em_object_mapping_list[i].action;
8565 if (em_object_mapping_list[i].direction != -1)
8566 object_mapping[e].direction =
8567 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8570 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8572 int a = em_player_mapping_list[i].action_em;
8573 int p = em_player_mapping_list[i].player_nr;
8575 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8577 if (em_player_mapping_list[i].action != -1)
8578 player_mapping[p][a].action = em_player_mapping_list[i].action;
8580 if (em_player_mapping_list[i].direction != -1)
8581 player_mapping[p][a].direction =
8582 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8585 for (i = 0; i < TILE_MAX; i++)
8587 int element = object_mapping[i].element_rnd;
8588 int action = object_mapping[i].action;
8589 int direction = object_mapping[i].direction;
8590 boolean is_backside = object_mapping[i].is_backside;
8591 boolean action_exploding = ((action == ACTION_EXPLODING ||
8592 action == ACTION_SMASHED_BY_ROCK ||
8593 action == ACTION_SMASHED_BY_SPRING) &&
8594 element != EL_DIAMOND);
8595 boolean action_active = (action == ACTION_ACTIVE);
8596 boolean action_other = (action == ACTION_OTHER);
8598 for (j = 0; j < 8; j++)
8600 int effective_element = get_effective_element_EM(i, j);
8601 int effective_action = (j < 7 ? action :
8602 i == Xdrip_stretch ? action :
8603 i == Xdrip_stretchB ? action :
8604 i == Ydrip_s1 ? action :
8605 i == Ydrip_s1B ? action :
8606 i == Xball_1B ? action :
8607 i == Xball_2 ? action :
8608 i == Xball_2B ? action :
8609 i == Yball_eat ? action :
8610 i == Ykey_1_eat ? action :
8611 i == Ykey_2_eat ? action :
8612 i == Ykey_3_eat ? action :
8613 i == Ykey_4_eat ? action :
8614 i == Ykey_5_eat ? action :
8615 i == Ykey_6_eat ? action :
8616 i == Ykey_7_eat ? action :
8617 i == Ykey_8_eat ? action :
8618 i == Ylenses_eat ? action :
8619 i == Ymagnify_eat ? action :
8620 i == Ygrass_eat ? action :
8621 i == Ydirt_eat ? action :
8622 i == Xsand_stonein_1 ? action :
8623 i == Xsand_stonein_2 ? action :
8624 i == Xsand_stonein_3 ? action :
8625 i == Xsand_stonein_4 ? action :
8626 i == Xsand_stoneout_1 ? action :
8627 i == Xsand_stoneout_2 ? action :
8628 i == Xboom_android ? ACTION_EXPLODING :
8629 action_exploding ? ACTION_EXPLODING :
8630 action_active ? action :
8631 action_other ? action :
8633 int graphic = (el_act_dir2img(effective_element, effective_action,
8635 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8637 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8638 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8639 boolean has_action_graphics = (graphic != base_graphic);
8640 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8641 struct GraphicInfo *g = &graphic_info[graphic];
8642 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8645 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8646 boolean special_animation = (action != ACTION_DEFAULT &&
8647 g->anim_frames == 3 &&
8648 g->anim_delay == 2 &&
8649 g->anim_mode & ANIM_LINEAR);
8650 int sync_frame = (i == Xdrip_stretch ? 7 :
8651 i == Xdrip_stretchB ? 7 :
8652 i == Ydrip_s2 ? j + 8 :
8653 i == Ydrip_s2B ? j + 8 :
8662 i == Xfake_acid_1 ? 0 :
8663 i == Xfake_acid_2 ? 10 :
8664 i == Xfake_acid_3 ? 20 :
8665 i == Xfake_acid_4 ? 30 :
8666 i == Xfake_acid_5 ? 40 :
8667 i == Xfake_acid_6 ? 50 :
8668 i == Xfake_acid_7 ? 60 :
8669 i == Xfake_acid_8 ? 70 :
8671 i == Xball_2B ? j + 8 :
8672 i == Yball_eat ? j + 1 :
8673 i == Ykey_1_eat ? j + 1 :
8674 i == Ykey_2_eat ? j + 1 :
8675 i == Ykey_3_eat ? j + 1 :
8676 i == Ykey_4_eat ? j + 1 :
8677 i == Ykey_5_eat ? j + 1 :
8678 i == Ykey_6_eat ? j + 1 :
8679 i == Ykey_7_eat ? j + 1 :
8680 i == Ykey_8_eat ? j + 1 :
8681 i == Ylenses_eat ? j + 1 :
8682 i == Ymagnify_eat ? j + 1 :
8683 i == Ygrass_eat ? j + 1 :
8684 i == Ydirt_eat ? j + 1 :
8685 i == Xamoeba_1 ? 0 :
8686 i == Xamoeba_2 ? 1 :
8687 i == Xamoeba_3 ? 2 :
8688 i == Xamoeba_4 ? 3 :
8689 i == Xamoeba_5 ? 0 :
8690 i == Xamoeba_6 ? 1 :
8691 i == Xamoeba_7 ? 2 :
8692 i == Xamoeba_8 ? 3 :
8693 i == Xexit_2 ? j + 8 :
8694 i == Xexit_3 ? j + 16 :
8695 i == Xdynamite_1 ? 0 :
8696 i == Xdynamite_2 ? 8 :
8697 i == Xdynamite_3 ? 16 :
8698 i == Xdynamite_4 ? 24 :
8699 i == Xsand_stonein_1 ? j + 1 :
8700 i == Xsand_stonein_2 ? j + 9 :
8701 i == Xsand_stonein_3 ? j + 17 :
8702 i == Xsand_stonein_4 ? j + 25 :
8703 i == Xsand_stoneout_1 && j == 0 ? 0 :
8704 i == Xsand_stoneout_1 && j == 1 ? 0 :
8705 i == Xsand_stoneout_1 && j == 2 ? 1 :
8706 i == Xsand_stoneout_1 && j == 3 ? 2 :
8707 i == Xsand_stoneout_1 && j == 4 ? 2 :
8708 i == Xsand_stoneout_1 && j == 5 ? 3 :
8709 i == Xsand_stoneout_1 && j == 6 ? 4 :
8710 i == Xsand_stoneout_1 && j == 7 ? 4 :
8711 i == Xsand_stoneout_2 && j == 0 ? 5 :
8712 i == Xsand_stoneout_2 && j == 1 ? 6 :
8713 i == Xsand_stoneout_2 && j == 2 ? 7 :
8714 i == Xsand_stoneout_2 && j == 3 ? 8 :
8715 i == Xsand_stoneout_2 && j == 4 ? 9 :
8716 i == Xsand_stoneout_2 && j == 5 ? 11 :
8717 i == Xsand_stoneout_2 && j == 6 ? 13 :
8718 i == Xsand_stoneout_2 && j == 7 ? 15 :
8719 i == Xboom_bug && j == 1 ? 2 :
8720 i == Xboom_bug && j == 2 ? 2 :
8721 i == Xboom_bug && j == 3 ? 4 :
8722 i == Xboom_bug && j == 4 ? 4 :
8723 i == Xboom_bug && j == 5 ? 2 :
8724 i == Xboom_bug && j == 6 ? 2 :
8725 i == Xboom_bug && j == 7 ? 0 :
8726 i == Xboom_bomb && j == 1 ? 2 :
8727 i == Xboom_bomb && j == 2 ? 2 :
8728 i == Xboom_bomb && j == 3 ? 4 :
8729 i == Xboom_bomb && j == 4 ? 4 :
8730 i == Xboom_bomb && j == 5 ? 2 :
8731 i == Xboom_bomb && j == 6 ? 2 :
8732 i == Xboom_bomb && j == 7 ? 0 :
8733 i == Xboom_android && j == 7 ? 6 :
8734 i == Xboom_1 && j == 1 ? 2 :
8735 i == Xboom_1 && j == 2 ? 2 :
8736 i == Xboom_1 && j == 3 ? 4 :
8737 i == Xboom_1 && j == 4 ? 4 :
8738 i == Xboom_1 && j == 5 ? 6 :
8739 i == Xboom_1 && j == 6 ? 6 :
8740 i == Xboom_1 && j == 7 ? 8 :
8741 i == Xboom_2 && j == 0 ? 8 :
8742 i == Xboom_2 && j == 1 ? 8 :
8743 i == Xboom_2 && j == 2 ? 10 :
8744 i == Xboom_2 && j == 3 ? 10 :
8745 i == Xboom_2 && j == 4 ? 10 :
8746 i == Xboom_2 && j == 5 ? 12 :
8747 i == Xboom_2 && j == 6 ? 12 :
8748 i == Xboom_2 && j == 7 ? 12 :
8749 special_animation && j == 4 ? 3 :
8750 effective_action != action ? 0 :
8754 Bitmap *debug_bitmap = g_em->bitmap;
8755 int debug_src_x = g_em->src_x;
8756 int debug_src_y = g_em->src_y;
8759 int frame = getAnimationFrame(g->anim_frames,
8762 g->anim_start_frame,
8765 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8766 g->double_movement && is_backside);
8768 g_em->bitmap = src_bitmap;
8769 g_em->src_x = src_x;
8770 g_em->src_y = src_y;
8771 g_em->src_offset_x = 0;
8772 g_em->src_offset_y = 0;
8773 g_em->dst_offset_x = 0;
8774 g_em->dst_offset_y = 0;
8775 g_em->width = TILEX;
8776 g_em->height = TILEY;
8778 g_em->preserve_background = FALSE;
8780 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8783 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8784 effective_action == ACTION_MOVING ||
8785 effective_action == ACTION_PUSHING ||
8786 effective_action == ACTION_EATING)) ||
8787 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8788 effective_action == ACTION_EMPTYING)))
8791 (effective_action == ACTION_FALLING ||
8792 effective_action == ACTION_FILLING ||
8793 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8794 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8795 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8796 int num_steps = (i == Ydrip_s1 ? 16 :
8797 i == Ydrip_s1B ? 16 :
8798 i == Ydrip_s2 ? 16 :
8799 i == Ydrip_s2B ? 16 :
8800 i == Xsand_stonein_1 ? 32 :
8801 i == Xsand_stonein_2 ? 32 :
8802 i == Xsand_stonein_3 ? 32 :
8803 i == Xsand_stonein_4 ? 32 :
8804 i == Xsand_stoneout_1 ? 16 :
8805 i == Xsand_stoneout_2 ? 16 : 8);
8806 int cx = ABS(dx) * (TILEX / num_steps);
8807 int cy = ABS(dy) * (TILEY / num_steps);
8808 int step_frame = (i == Ydrip_s2 ? j + 8 :
8809 i == Ydrip_s2B ? j + 8 :
8810 i == Xsand_stonein_2 ? j + 8 :
8811 i == Xsand_stonein_3 ? j + 16 :
8812 i == Xsand_stonein_4 ? j + 24 :
8813 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8814 int step = (is_backside ? step_frame : num_steps - step_frame);
8816 if (is_backside) // tile where movement starts
8818 if (dx < 0 || dy < 0)
8820 g_em->src_offset_x = cx * step;
8821 g_em->src_offset_y = cy * step;
8825 g_em->dst_offset_x = cx * step;
8826 g_em->dst_offset_y = cy * step;
8829 else // tile where movement ends
8831 if (dx < 0 || dy < 0)
8833 g_em->dst_offset_x = cx * step;
8834 g_em->dst_offset_y = cy * step;
8838 g_em->src_offset_x = cx * step;
8839 g_em->src_offset_y = cy * step;
8843 g_em->width = TILEX - cx * step;
8844 g_em->height = TILEY - cy * step;
8847 // create unique graphic identifier to decide if tile must be redrawn
8848 /* bit 31 - 16 (16 bit): EM style graphic
8849 bit 15 - 12 ( 4 bit): EM style frame
8850 bit 11 - 6 ( 6 bit): graphic width
8851 bit 5 - 0 ( 6 bit): graphic height */
8852 g_em->unique_identifier =
8853 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8857 // skip check for EMC elements not contained in original EMC artwork
8858 if (element == EL_EMC_FAKE_ACID)
8861 if (g_em->bitmap != debug_bitmap ||
8862 g_em->src_x != debug_src_x ||
8863 g_em->src_y != debug_src_y ||
8864 g_em->src_offset_x != 0 ||
8865 g_em->src_offset_y != 0 ||
8866 g_em->dst_offset_x != 0 ||
8867 g_em->dst_offset_y != 0 ||
8868 g_em->width != TILEX ||
8869 g_em->height != TILEY)
8871 static int last_i = -1;
8879 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8880 i, element, element_info[element].token_name,
8881 element_action_info[effective_action].suffix, direction);
8883 if (element != effective_element)
8884 printf(" [%d ('%s')]",
8886 element_info[effective_element].token_name);
8890 if (g_em->bitmap != debug_bitmap)
8891 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8892 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8894 if (g_em->src_x != debug_src_x ||
8895 g_em->src_y != debug_src_y)
8896 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8897 j, (is_backside ? 'B' : 'F'),
8898 g_em->src_x, g_em->src_y,
8899 g_em->src_x / 32, g_em->src_y / 32,
8900 debug_src_x, debug_src_y,
8901 debug_src_x / 32, debug_src_y / 32);
8903 if (g_em->src_offset_x != 0 ||
8904 g_em->src_offset_y != 0 ||
8905 g_em->dst_offset_x != 0 ||
8906 g_em->dst_offset_y != 0)
8907 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8909 g_em->src_offset_x, g_em->src_offset_y,
8910 g_em->dst_offset_x, g_em->dst_offset_y);
8912 if (g_em->width != TILEX ||
8913 g_em->height != TILEY)
8914 printf(" %d (%d): size %d,%d should be %d,%d\n",
8916 g_em->width, g_em->height, TILEX, TILEY);
8918 num_em_gfx_errors++;
8925 for (i = 0; i < TILE_MAX; i++)
8927 for (j = 0; j < 8; j++)
8929 int element = object_mapping[i].element_rnd;
8930 int action = object_mapping[i].action;
8931 int direction = object_mapping[i].direction;
8932 boolean is_backside = object_mapping[i].is_backside;
8933 int graphic_action = el_act_dir2img(element, action, direction);
8934 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8936 if ((action == ACTION_SMASHED_BY_ROCK ||
8937 action == ACTION_SMASHED_BY_SPRING ||
8938 action == ACTION_EATING) &&
8939 graphic_action == graphic_default)
8941 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8942 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8943 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8944 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8947 // no separate animation for "smashed by rock" -- use rock instead
8948 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8949 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8951 g_em->bitmap = g_xx->bitmap;
8952 g_em->src_x = g_xx->src_x;
8953 g_em->src_y = g_xx->src_y;
8954 g_em->src_offset_x = g_xx->src_offset_x;
8955 g_em->src_offset_y = g_xx->src_offset_y;
8956 g_em->dst_offset_x = g_xx->dst_offset_x;
8957 g_em->dst_offset_y = g_xx->dst_offset_y;
8958 g_em->width = g_xx->width;
8959 g_em->height = g_xx->height;
8960 g_em->unique_identifier = g_xx->unique_identifier;
8963 g_em->preserve_background = TRUE;
8968 for (p = 0; p < MAX_PLAYERS; p++)
8970 for (i = 0; i < SPR_MAX; i++)
8972 int element = player_mapping[p][i].element_rnd;
8973 int action = player_mapping[p][i].action;
8974 int direction = player_mapping[p][i].direction;
8976 for (j = 0; j < 8; j++)
8978 int effective_element = element;
8979 int effective_action = action;
8980 int graphic = (direction == MV_NONE ?
8981 el_act2img(effective_element, effective_action) :
8982 el_act_dir2img(effective_element, effective_action,
8984 struct GraphicInfo *g = &graphic_info[graphic];
8985 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8991 Bitmap *debug_bitmap = g_em->bitmap;
8992 int debug_src_x = g_em->src_x;
8993 int debug_src_y = g_em->src_y;
8996 int frame = getAnimationFrame(g->anim_frames,
8999 g->anim_start_frame,
9002 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
9004 g_em->bitmap = src_bitmap;
9005 g_em->src_x = src_x;
9006 g_em->src_y = src_y;
9007 g_em->src_offset_x = 0;
9008 g_em->src_offset_y = 0;
9009 g_em->dst_offset_x = 0;
9010 g_em->dst_offset_y = 0;
9011 g_em->width = TILEX;
9012 g_em->height = TILEY;
9016 // skip check for EMC elements not contained in original EMC artwork
9017 if (element == EL_PLAYER_3 ||
9018 element == EL_PLAYER_4)
9021 if (g_em->bitmap != debug_bitmap ||
9022 g_em->src_x != debug_src_x ||
9023 g_em->src_y != debug_src_y)
9025 static int last_i = -1;
9033 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9034 p, i, element, element_info[element].token_name,
9035 element_action_info[effective_action].suffix, direction);
9037 if (element != effective_element)
9038 printf(" [%d ('%s')]",
9040 element_info[effective_element].token_name);
9044 if (g_em->bitmap != debug_bitmap)
9045 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9046 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9048 if (g_em->src_x != debug_src_x ||
9049 g_em->src_y != debug_src_y)
9050 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9052 g_em->src_x, g_em->src_y,
9053 g_em->src_x / 32, g_em->src_y / 32,
9054 debug_src_x, debug_src_y,
9055 debug_src_x / 32, debug_src_y / 32);
9057 num_em_gfx_errors++;
9067 printf("::: [%d errors found]\n", num_em_gfx_errors);
9073 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9074 boolean any_player_moving,
9075 boolean any_player_snapping,
9076 boolean any_player_dropping)
9078 if (frame == 0 && !any_player_dropping)
9080 if (!local_player->was_waiting)
9082 if (!CheckSaveEngineSnapshotToList())
9085 local_player->was_waiting = TRUE;
9088 else if (any_player_moving || any_player_snapping || any_player_dropping)
9090 local_player->was_waiting = FALSE;
9094 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9095 boolean murphy_is_dropping)
9097 if (murphy_is_waiting)
9099 if (!local_player->was_waiting)
9101 if (!CheckSaveEngineSnapshotToList())
9104 local_player->was_waiting = TRUE;
9109 local_player->was_waiting = FALSE;
9113 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9114 boolean button_released)
9116 if (button_released)
9118 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9119 CheckSaveEngineSnapshotToList();
9121 else if (element_clicked)
9123 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9124 CheckSaveEngineSnapshotToList();
9126 game.snapshot.changed_action = TRUE;
9130 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9131 boolean any_player_moving,
9132 boolean any_player_snapping,
9133 boolean any_player_dropping)
9135 if (tape.single_step && tape.recording && !tape.pausing)
9136 if (frame == 0 && !any_player_dropping)
9137 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9139 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9140 any_player_snapping, any_player_dropping);
9143 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9144 boolean murphy_is_dropping)
9146 boolean murphy_starts_dropping = FALSE;
9149 for (i = 0; i < MAX_PLAYERS; i++)
9150 if (stored_player[i].force_dropping)
9151 murphy_starts_dropping = TRUE;
9153 if (tape.single_step && tape.recording && !tape.pausing)
9154 if (murphy_is_waiting && !murphy_starts_dropping)
9155 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9157 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9160 void CheckSingleStepMode_MM(boolean element_clicked,
9161 boolean button_released)
9163 if (tape.single_step && tape.recording && !tape.pausing)
9164 if (button_released)
9165 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9167 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9170 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9171 int graphic, int sync_frame, int x, int y)
9173 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9175 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9178 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9180 return (IS_NEXT_FRAME(sync_frame, graphic));
9183 int getGraphicInfo_Delay(int graphic)
9185 return graphic_info[graphic].anim_delay;
9188 void PlayMenuSoundExt(int sound)
9190 if (sound == SND_UNDEFINED)
9193 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9194 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9197 if (IS_LOOP_SOUND(sound))
9198 PlaySoundLoop(sound);
9203 void PlayMenuSound(void)
9205 PlayMenuSoundExt(menu.sound[game_status]);
9208 void PlayMenuSoundStereo(int sound, int stereo_position)
9210 if (sound == SND_UNDEFINED)
9213 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9214 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9217 if (IS_LOOP_SOUND(sound))
9218 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9220 PlaySoundStereo(sound, stereo_position);
9223 void PlayMenuSoundIfLoopExt(int sound)
9225 if (sound == SND_UNDEFINED)
9228 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9229 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9232 if (IS_LOOP_SOUND(sound))
9233 PlaySoundLoop(sound);
9236 void PlayMenuSoundIfLoop(void)
9238 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9241 void PlayMenuMusicExt(int music)
9243 if (music == MUS_UNDEFINED)
9246 if (!setup.sound_music)
9249 if (IS_LOOP_MUSIC(music))
9250 PlayMusicLoop(music);
9255 void PlayMenuMusic(void)
9257 char *curr_music = getCurrentlyPlayingMusicFilename();
9258 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9260 if (!strEqual(curr_music, next_music))
9261 PlayMenuMusicExt(menu.music[game_status]);
9264 void PlayMenuSoundsAndMusic(void)
9270 static void FadeMenuSounds(void)
9275 static void FadeMenuMusic(void)
9277 char *curr_music = getCurrentlyPlayingMusicFilename();
9278 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9280 if (!strEqual(curr_music, next_music))
9284 void FadeMenuSoundsAndMusic(void)
9290 void PlaySoundActivating(void)
9293 PlaySound(SND_MENU_ITEM_ACTIVATING);
9297 void PlaySoundSelecting(void)
9300 PlaySound(SND_MENU_ITEM_SELECTING);
9304 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9306 boolean change_fullscreen = (setup.fullscreen !=
9307 video.fullscreen_enabled);
9308 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9309 setup.window_scaling_percent !=
9310 video.window_scaling_percent);
9312 if (change_window_scaling_percent && video.fullscreen_enabled)
9315 if (!change_window_scaling_percent && !video.fullscreen_available)
9318 if (change_window_scaling_percent)
9320 SDLSetWindowScaling(setup.window_scaling_percent);
9324 else if (change_fullscreen)
9326 SDLSetWindowFullscreen(setup.fullscreen);
9328 // set setup value according to successfully changed fullscreen mode
9329 setup.fullscreen = video.fullscreen_enabled;
9334 if (change_fullscreen ||
9335 change_window_scaling_percent)
9337 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9339 // save backbuffer content which gets lost when toggling fullscreen mode
9340 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9342 if (change_window_scaling_percent)
9344 // keep window mode, but change window scaling
9345 video.fullscreen_enabled = TRUE; // force new window scaling
9348 // toggle fullscreen
9349 ChangeVideoModeIfNeeded(setup.fullscreen);
9351 // set setup value according to successfully changed fullscreen mode
9352 setup.fullscreen = video.fullscreen_enabled;
9354 // restore backbuffer content from temporary backbuffer backup bitmap
9355 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9357 FreeBitmap(tmp_backbuffer);
9359 // update visible window/screen
9360 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9364 static void JoinRectangles(int *x, int *y, int *width, int *height,
9365 int x2, int y2, int width2, int height2)
9367 // do not join with "off-screen" rectangle
9368 if (x2 == -1 || y2 == -1)
9373 *width = MAX(*width, width2);
9374 *height = MAX(*height, height2);
9377 void SetAnimStatus(int anim_status_new)
9379 if (anim_status_new == GAME_MODE_MAIN)
9380 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9381 else if (anim_status_new == GAME_MODE_SCORES)
9382 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9384 global.anim_status_next = anim_status_new;
9386 // directly set screen modes that are entered without fading
9387 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9388 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9389 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9390 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9391 global.anim_status = global.anim_status_next;
9394 void SetGameStatus(int game_status_new)
9396 if (game_status_new != game_status)
9397 game_status_last_screen = game_status;
9399 game_status = game_status_new;
9401 SetAnimStatus(game_status_new);
9404 void SetFontStatus(int game_status_new)
9406 static int last_game_status = -1;
9408 if (game_status_new != -1)
9410 // set game status for font use after storing last game status
9411 last_game_status = game_status;
9412 game_status = game_status_new;
9416 // reset game status after font use from last stored game status
9417 game_status = last_game_status;
9421 void ResetFontStatus(void)
9426 void SetLevelSetInfo(char *identifier, int level_nr)
9428 setString(&levelset.identifier, identifier);
9430 levelset.level_nr = level_nr;
9433 boolean CheckIfAllViewportsHaveChanged(void)
9435 // if game status has not changed, viewports have not changed either
9436 if (game_status == game_status_last)
9439 // check if all viewports have changed with current game status
9441 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9442 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9443 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9444 int new_real_sx = vp_playfield->x;
9445 int new_real_sy = vp_playfield->y;
9446 int new_full_sxsize = vp_playfield->width;
9447 int new_full_sysize = vp_playfield->height;
9448 int new_dx = vp_door_1->x;
9449 int new_dy = vp_door_1->y;
9450 int new_dxsize = vp_door_1->width;
9451 int new_dysize = vp_door_1->height;
9452 int new_vx = vp_door_2->x;
9453 int new_vy = vp_door_2->y;
9454 int new_vxsize = vp_door_2->width;
9455 int new_vysize = vp_door_2->height;
9457 boolean playfield_viewport_has_changed =
9458 (new_real_sx != REAL_SX ||
9459 new_real_sy != REAL_SY ||
9460 new_full_sxsize != FULL_SXSIZE ||
9461 new_full_sysize != FULL_SYSIZE);
9463 boolean door_1_viewport_has_changed =
9466 new_dxsize != DXSIZE ||
9467 new_dysize != DYSIZE);
9469 boolean door_2_viewport_has_changed =
9472 new_vxsize != VXSIZE ||
9473 new_vysize != VYSIZE ||
9474 game_status_last == GAME_MODE_EDITOR);
9476 return (playfield_viewport_has_changed &&
9477 door_1_viewport_has_changed &&
9478 door_2_viewport_has_changed);
9481 boolean CheckFadeAll(void)
9483 return (CheckIfGlobalBorderHasChanged() ||
9484 CheckIfAllViewportsHaveChanged());
9487 void ChangeViewportPropertiesIfNeeded(void)
9489 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9490 FALSE : setup.small_game_graphics);
9491 int gfx_game_mode = game_status;
9492 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9494 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9495 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9496 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9497 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9498 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9499 int new_win_xsize = vp_window->width;
9500 int new_win_ysize = vp_window->height;
9501 int border_left = vp_playfield->border_left;
9502 int border_right = vp_playfield->border_right;
9503 int border_top = vp_playfield->border_top;
9504 int border_bottom = vp_playfield->border_bottom;
9505 int new_sx = vp_playfield->x + border_left;
9506 int new_sy = vp_playfield->y + border_top;
9507 int new_sxsize = vp_playfield->width - border_left - border_right;
9508 int new_sysize = vp_playfield->height - border_top - border_bottom;
9509 int new_real_sx = vp_playfield->x;
9510 int new_real_sy = vp_playfield->y;
9511 int new_full_sxsize = vp_playfield->width;
9512 int new_full_sysize = vp_playfield->height;
9513 int new_dx = vp_door_1->x;
9514 int new_dy = vp_door_1->y;
9515 int new_dxsize = vp_door_1->width;
9516 int new_dysize = vp_door_1->height;
9517 int new_vx = vp_door_2->x;
9518 int new_vy = vp_door_2->y;
9519 int new_vxsize = vp_door_2->width;
9520 int new_vysize = vp_door_2->height;
9521 int new_ex = vp_door_3->x;
9522 int new_ey = vp_door_3->y;
9523 int new_exsize = vp_door_3->width;
9524 int new_eysize = vp_door_3->height;
9525 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9526 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9527 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9528 int new_scr_fieldx = new_sxsize / tilesize;
9529 int new_scr_fieldy = new_sysize / tilesize;
9530 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9531 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9532 boolean init_gfx_buffers = FALSE;
9533 boolean init_video_buffer = FALSE;
9534 boolean init_gadgets_and_anims = FALSE;
9535 boolean init_em_graphics = FALSE;
9537 if (new_win_xsize != WIN_XSIZE ||
9538 new_win_ysize != WIN_YSIZE)
9540 WIN_XSIZE = new_win_xsize;
9541 WIN_YSIZE = new_win_ysize;
9543 init_video_buffer = TRUE;
9544 init_gfx_buffers = TRUE;
9545 init_gadgets_and_anims = TRUE;
9547 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9550 if (new_scr_fieldx != SCR_FIELDX ||
9551 new_scr_fieldy != SCR_FIELDY)
9553 // this always toggles between MAIN and GAME when using small tile size
9555 SCR_FIELDX = new_scr_fieldx;
9556 SCR_FIELDY = new_scr_fieldy;
9558 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9569 new_sxsize != SXSIZE ||
9570 new_sysize != SYSIZE ||
9571 new_dxsize != DXSIZE ||
9572 new_dysize != DYSIZE ||
9573 new_vxsize != VXSIZE ||
9574 new_vysize != VYSIZE ||
9575 new_exsize != EXSIZE ||
9576 new_eysize != EYSIZE ||
9577 new_real_sx != REAL_SX ||
9578 new_real_sy != REAL_SY ||
9579 new_full_sxsize != FULL_SXSIZE ||
9580 new_full_sysize != FULL_SYSIZE ||
9581 new_tilesize_var != TILESIZE_VAR
9584 // ------------------------------------------------------------------------
9585 // determine next fading area for changed viewport definitions
9586 // ------------------------------------------------------------------------
9588 // start with current playfield area (default fading area)
9591 FADE_SXSIZE = FULL_SXSIZE;
9592 FADE_SYSIZE = FULL_SYSIZE;
9594 // add new playfield area if position or size has changed
9595 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9596 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9598 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9599 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9602 // add current and new door 1 area if position or size has changed
9603 if (new_dx != DX || new_dy != DY ||
9604 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9606 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9607 DX, DY, DXSIZE, DYSIZE);
9608 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9609 new_dx, new_dy, new_dxsize, new_dysize);
9612 // add current and new door 2 area if position or size has changed
9613 if (new_vx != VX || new_vy != VY ||
9614 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9616 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9617 VX, VY, VXSIZE, VYSIZE);
9618 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9619 new_vx, new_vy, new_vxsize, new_vysize);
9622 // ------------------------------------------------------------------------
9623 // handle changed tile size
9624 // ------------------------------------------------------------------------
9626 if (new_tilesize_var != TILESIZE_VAR)
9628 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9630 // changing tile size invalidates scroll values of engine snapshots
9631 FreeEngineSnapshotSingle();
9633 // changing tile size requires update of graphic mapping for EM engine
9634 init_em_graphics = TRUE;
9645 SXSIZE = new_sxsize;
9646 SYSIZE = new_sysize;
9647 DXSIZE = new_dxsize;
9648 DYSIZE = new_dysize;
9649 VXSIZE = new_vxsize;
9650 VYSIZE = new_vysize;
9651 EXSIZE = new_exsize;
9652 EYSIZE = new_eysize;
9653 REAL_SX = new_real_sx;
9654 REAL_SY = new_real_sy;
9655 FULL_SXSIZE = new_full_sxsize;
9656 FULL_SYSIZE = new_full_sysize;
9657 TILESIZE_VAR = new_tilesize_var;
9659 init_gfx_buffers = TRUE;
9660 init_gadgets_and_anims = TRUE;
9662 // printf("::: viewports: init_gfx_buffers\n");
9663 // printf("::: viewports: init_gadgets_and_anims\n");
9666 if (init_gfx_buffers)
9668 // printf("::: init_gfx_buffers\n");
9670 SCR_FIELDX = new_scr_fieldx_buffers;
9671 SCR_FIELDY = new_scr_fieldy_buffers;
9675 SCR_FIELDX = new_scr_fieldx;
9676 SCR_FIELDY = new_scr_fieldy;
9678 SetDrawDeactivationMask(REDRAW_NONE);
9679 SetDrawBackgroundMask(REDRAW_FIELD);
9682 if (init_video_buffer)
9684 // printf("::: init_video_buffer\n");
9686 FreeAllImageTextures(); // needs old renderer to free the textures
9688 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9689 InitImageTextures();
9692 if (init_gadgets_and_anims)
9694 // printf("::: init_gadgets_and_anims\n");
9697 InitGlobalAnimations();
9700 if (init_em_graphics)
9702 InitGraphicInfo_EM();