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 static int getFieldbufferOffsetX_RND(void)
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND(void)
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
363 int level_xsize = level.native_mm_level->fieldx;
364 int full_xsize = level_xsize * TILESIZE_VAR;
366 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
369 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
374 static int getLevelFromScreenY_MM(int sy)
376 int level_ysize = level.native_mm_level->fieldy;
377 int full_ysize = level_ysize * TILESIZE_VAR;
379 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
382 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
387 int getLevelFromScreenX(int x)
389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
390 return getLevelFromScreenX_EM(x);
391 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
392 return getLevelFromScreenX_SP(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
394 return getLevelFromScreenX_MM(x);
396 return getLevelFromScreenX_RND(x);
399 int getLevelFromScreenY(int y)
401 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
402 return getLevelFromScreenY_EM(y);
403 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
404 return getLevelFromScreenY_SP(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
406 return getLevelFromScreenY_MM(y);
408 return getLevelFromScreenY_RND(y);
411 void DumpTile(int x, int y)
417 printf_line("-", 79);
418 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
419 printf_line("-", 79);
421 if (!IN_LEV_FIELD(x, y))
423 printf("(not in level field)\n");
429 token_name = element_info[Feld[x][y]].token_name;
431 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
432 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
433 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
434 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
435 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
436 printf(" MovPos: %d\n", MovPos[x][y]);
437 printf(" MovDir: %d\n", MovDir[x][y]);
438 printf(" MovDelay: %d\n", MovDelay[x][y]);
439 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
440 printf(" CustomValue: %d\n", CustomValue[x][y]);
441 printf(" GfxElement: %d\n", GfxElement[x][y]);
442 printf(" GfxAction: %d\n", GfxAction[x][y]);
443 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
444 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
448 void DumpTileFromScreen(int sx, int sy)
450 int lx = getLevelFromScreenX(sx);
451 int ly = getLevelFromScreenY(sy);
456 void SetDrawtoField(int mode)
458 if (mode == DRAW_TO_FIELDBUFFER)
464 BX2 = SCR_FIELDX + 1;
465 BY2 = SCR_FIELDY + 1;
467 drawto_field = fieldbuffer;
469 else // DRAW_TO_BACKBUFFER
475 BX2 = SCR_FIELDX - 1;
476 BY2 = SCR_FIELDY - 1;
478 drawto_field = backbuffer;
482 static void RedrawPlayfield_RND(void)
484 if (game.envelope_active)
487 DrawLevel(REDRAW_ALL);
491 void RedrawPlayfield(void)
493 if (game_status != GAME_MODE_PLAYING)
496 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
497 RedrawPlayfield_EM(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
499 RedrawPlayfield_SP(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
501 RedrawPlayfield_MM();
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
503 RedrawPlayfield_RND();
505 BlitScreenToBitmap(backbuffer);
507 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
511 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
514 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
515 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
517 if (x == -1 && y == -1)
520 if (draw_target == DRAW_TO_SCREEN)
521 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
523 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
526 static void DrawMaskedBorderExt_FIELD(int draw_target)
528 if (global.border_status >= GAME_MODE_MAIN &&
529 global.border_status <= GAME_MODE_PLAYING &&
530 border.draw_masked[global.border_status])
531 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
535 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
537 // when drawing to backbuffer, never draw border over open doors
538 if (draw_target == DRAW_TO_BACKBUFFER &&
539 (GetDoorState() & DOOR_OPEN_1))
542 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
543 (global.border_status != GAME_MODE_EDITOR ||
544 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
545 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
548 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
550 // when drawing to backbuffer, never draw border over open doors
551 if (draw_target == DRAW_TO_BACKBUFFER &&
552 (GetDoorState() & DOOR_OPEN_2))
555 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556 global.border_status != GAME_MODE_EDITOR)
557 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
560 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
562 // currently not available
565 static void DrawMaskedBorderExt_ALL(int draw_target)
567 DrawMaskedBorderExt_FIELD(draw_target);
568 DrawMaskedBorderExt_DOOR_1(draw_target);
569 DrawMaskedBorderExt_DOOR_2(draw_target);
570 DrawMaskedBorderExt_DOOR_3(draw_target);
573 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
575 // never draw masked screen borders on borderless screens
576 if (global.border_status == GAME_MODE_LOADING ||
577 global.border_status == GAME_MODE_TITLE)
580 if (redraw_mask & REDRAW_ALL)
581 DrawMaskedBorderExt_ALL(draw_target);
584 if (redraw_mask & REDRAW_FIELD)
585 DrawMaskedBorderExt_FIELD(draw_target);
586 if (redraw_mask & REDRAW_DOOR_1)
587 DrawMaskedBorderExt_DOOR_1(draw_target);
588 if (redraw_mask & REDRAW_DOOR_2)
589 DrawMaskedBorderExt_DOOR_2(draw_target);
590 if (redraw_mask & REDRAW_DOOR_3)
591 DrawMaskedBorderExt_DOOR_3(draw_target);
595 void DrawMaskedBorder_FIELD(void)
597 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
600 void DrawMaskedBorder(int redraw_mask)
602 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
605 void DrawMaskedBorderToTarget(int draw_target)
607 if (draw_target == DRAW_TO_BACKBUFFER ||
608 draw_target == DRAW_TO_SCREEN)
610 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
614 int last_border_status = global.border_status;
616 if (draw_target == DRAW_TO_FADE_SOURCE)
618 global.border_status = gfx.fade_border_source_status;
619 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
621 else if (draw_target == DRAW_TO_FADE_TARGET)
623 global.border_status = gfx.fade_border_target_status;
624 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
627 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
629 global.border_status = last_border_status;
630 gfx.masked_border_bitmap_ptr = backbuffer;
634 void DrawTileCursor(int draw_target)
640 int graphic = IMG_GLOBAL_TILE_CURSOR;
642 int tilesize = TILESIZE_VAR;
643 int width = tilesize;
644 int height = tilesize;
646 if (game_status != GAME_MODE_PLAYING)
649 if (!tile_cursor.enabled ||
653 if (tile_cursor.moving)
655 int step = TILESIZE_VAR / 4;
656 int dx = tile_cursor.target_x - tile_cursor.x;
657 int dy = tile_cursor.target_y - tile_cursor.y;
660 tile_cursor.x = tile_cursor.target_x;
662 tile_cursor.x += SIGN(dx) * step;
665 tile_cursor.y = tile_cursor.target_y;
667 tile_cursor.y += SIGN(dy) * step;
669 if (tile_cursor.x == tile_cursor.target_x &&
670 tile_cursor.y == tile_cursor.target_y)
671 tile_cursor.moving = FALSE;
674 dst_x = tile_cursor.x;
675 dst_y = tile_cursor.y;
677 frame = getGraphicAnimationFrame(graphic, -1);
679 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
682 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
683 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
685 if (draw_target == DRAW_TO_SCREEN)
686 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
688 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
692 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
694 int fx = getFieldbufferOffsetX_RND();
695 int fy = getFieldbufferOffsetY_RND();
697 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
700 void BlitScreenToBitmap(Bitmap *target_bitmap)
702 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
703 BlitScreenToBitmap_EM(target_bitmap);
704 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
705 BlitScreenToBitmap_SP(target_bitmap);
706 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
707 BlitScreenToBitmap_MM(target_bitmap);
708 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
709 BlitScreenToBitmap_RND(target_bitmap);
711 redraw_mask |= REDRAW_FIELD;
714 static void DrawFramesPerSecond(void)
717 int font_nr = FONT_TEXT_2;
718 int font_width = getFontWidth(font_nr);
719 int draw_deactivation_mask = GetDrawDeactivationMask();
720 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
722 // draw FPS with leading space (needed if field buffer deactivated)
723 sprintf(text, " %04.1f fps", global.frames_per_second);
725 // override draw deactivation mask (required for invisible warp mode)
726 SetDrawDeactivationMask(REDRAW_NONE);
728 // draw opaque FPS if field buffer deactivated, else draw masked FPS
729 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
730 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
732 // set draw deactivation mask to previous value
733 SetDrawDeactivationMask(draw_deactivation_mask);
735 // force full-screen redraw in this frame
736 redraw_mask = REDRAW_ALL;
740 static void PrintFrameTimeDebugging(void)
742 static unsigned int last_counter = 0;
743 unsigned int counter = Counter();
744 int diff_1 = counter - last_counter;
745 int diff_2 = diff_1 - GAME_FRAME_DELAY;
747 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
748 char diff_bar[2 * diff_2_max + 5];
752 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
754 for (i = 0; i < diff_2_max; i++)
755 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
756 i >= diff_2_max - diff_2_cut ? '-' : ' ');
758 diff_bar[pos++] = '|';
760 for (i = 0; i < diff_2_max; i++)
761 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
763 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
765 diff_bar[pos++] = '\0';
767 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
770 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
773 last_counter = counter;
777 static int unifiedRedrawMask(int mask)
779 if (mask & REDRAW_ALL)
782 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
788 static boolean equalRedrawMasks(int mask_1, int mask_2)
790 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
793 void BackToFront(void)
795 static int last_redraw_mask = REDRAW_NONE;
797 // force screen redraw in every frame to continue drawing global animations
798 // (but always use the last redraw mask to prevent unwanted side effects)
799 if (redraw_mask == REDRAW_NONE)
800 redraw_mask = last_redraw_mask;
802 last_redraw_mask = redraw_mask;
805 // masked border now drawn immediately when blitting backbuffer to window
807 // draw masked border to all viewports, if defined
808 DrawMaskedBorder(redraw_mask);
811 // draw frames per second (only if debug mode is enabled)
812 if (redraw_mask & REDRAW_FPS)
813 DrawFramesPerSecond();
815 // remove playfield redraw before potentially merging with doors redraw
816 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
817 redraw_mask &= ~REDRAW_FIELD;
819 // redraw complete window if both playfield and (some) doors need redraw
820 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
821 redraw_mask = REDRAW_ALL;
823 /* although redrawing the whole window would be fine for normal gameplay,
824 being able to only redraw the playfield is required for deactivating
825 certain drawing areas (mainly playfield) to work, which is needed for
826 warp-forward to be fast enough (by skipping redraw of most frames) */
828 if (redraw_mask & REDRAW_ALL)
830 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
832 else if (redraw_mask & REDRAW_FIELD)
834 BlitBitmap(backbuffer, window,
835 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
837 else if (redraw_mask & REDRAW_DOORS)
839 // merge door areas to prevent calling screen redraw more than once
845 if (redraw_mask & REDRAW_DOOR_1)
849 x2 = MAX(x2, DX + DXSIZE);
850 y2 = MAX(y2, DY + DYSIZE);
853 if (redraw_mask & REDRAW_DOOR_2)
857 x2 = MAX(x2, VX + VXSIZE);
858 y2 = MAX(y2, VY + VYSIZE);
861 if (redraw_mask & REDRAW_DOOR_3)
865 x2 = MAX(x2, EX + EXSIZE);
866 y2 = MAX(y2, EY + EYSIZE);
869 // make sure that at least one pixel is blitted, and inside the screen
870 // (else nothing is blitted, causing the animations not to be updated)
871 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
872 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
873 x2 = MIN(MAX(1, x2), WIN_XSIZE);
874 y2 = MIN(MAX(1, y2), WIN_YSIZE);
876 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
879 redraw_mask = REDRAW_NONE;
882 PrintFrameTimeDebugging();
886 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
888 unsigned int frame_delay_value_old = GetVideoFrameDelay();
890 SetVideoFrameDelay(frame_delay_value);
894 SetVideoFrameDelay(frame_delay_value_old);
897 static int fade_type_skip = FADE_TYPE_NONE;
899 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
901 void (*draw_border_function)(void) = NULL;
902 int x, y, width, height;
903 int fade_delay, post_delay;
905 if (fade_type == FADE_TYPE_FADE_OUT)
907 if (fade_type_skip != FADE_TYPE_NONE)
909 // skip all fade operations until specified fade operation
910 if (fade_type & fade_type_skip)
911 fade_type_skip = FADE_TYPE_NONE;
916 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
920 redraw_mask |= fade_mask;
922 if (fade_type == FADE_TYPE_SKIP)
924 fade_type_skip = fade_mode;
929 fade_delay = fading.fade_delay;
930 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
932 if (fade_type_skip != FADE_TYPE_NONE)
934 // skip all fade operations until specified fade operation
935 if (fade_type & fade_type_skip)
936 fade_type_skip = FADE_TYPE_NONE;
941 if (global.autoplay_leveldir)
946 if (fade_mask == REDRAW_FIELD)
951 height = FADE_SYSIZE;
953 if (border.draw_masked_when_fading)
954 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
956 DrawMaskedBorder_FIELD(); // draw once
966 if (!setup.fade_screens ||
968 fading.fade_mode == FADE_MODE_NONE)
970 if (fade_mode == FADE_MODE_FADE_OUT)
973 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
975 redraw_mask &= ~fade_mask;
980 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
981 draw_border_function);
983 redraw_mask &= ~fade_mask;
985 ClearAutoRepeatKeyEvents();
988 static void SetScreenStates_BeforeFadingIn(void)
990 // temporarily set screen mode for animations to screen after fading in
991 global.anim_status = global.anim_status_next;
993 // store backbuffer with all animations that will be started after fading in
994 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
995 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
997 // set screen mode for animations back to fading
998 global.anim_status = GAME_MODE_PSEUDO_FADING;
1001 static void SetScreenStates_AfterFadingIn(void)
1003 // store new source screen (to use correct masked border for fading)
1004 gfx.fade_border_source_status = global.border_status;
1006 global.anim_status = global.anim_status_next;
1009 static void SetScreenStates_BeforeFadingOut(void)
1011 // store new target screen (to use correct masked border for fading)
1012 gfx.fade_border_target_status = game_status;
1014 // set screen mode for animations to fading
1015 global.anim_status = GAME_MODE_PSEUDO_FADING;
1017 // store backbuffer with all animations that will be stopped for fading out
1018 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1019 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1022 static void SetScreenStates_AfterFadingOut(void)
1024 global.border_status = game_status;
1027 void FadeIn(int fade_mask)
1029 SetScreenStates_BeforeFadingIn();
1032 DrawMaskedBorder(REDRAW_ALL);
1035 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1036 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1038 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1042 FADE_SXSIZE = FULL_SXSIZE;
1043 FADE_SYSIZE = FULL_SYSIZE;
1045 if (game_status == GAME_MODE_PLAYING &&
1046 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1047 SetOverlayActive(TRUE);
1049 SetScreenStates_AfterFadingIn();
1051 // force update of global animation status in case of rapid screen changes
1052 redraw_mask = REDRAW_ALL;
1056 void FadeOut(int fade_mask)
1058 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1059 if (!equalRedrawMasks(fade_mask, redraw_mask))
1062 SetScreenStates_BeforeFadingOut();
1064 SetTileCursorActive(FALSE);
1065 SetOverlayActive(FALSE);
1068 DrawMaskedBorder(REDRAW_ALL);
1071 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1072 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1074 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1076 SetScreenStates_AfterFadingOut();
1079 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1081 static struct TitleFadingInfo fading_leave_stored;
1084 fading_leave_stored = fading_leave;
1086 fading = fading_leave_stored;
1089 void FadeSetEnterMenu(void)
1091 fading = menu.enter_menu;
1093 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1096 void FadeSetLeaveMenu(void)
1098 fading = menu.leave_menu;
1100 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1103 void FadeSetEnterScreen(void)
1105 fading = menu.enter_screen[game_status];
1107 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1110 void FadeSetNextScreen(void)
1112 fading = menu.next_screen[game_status];
1114 // (do not overwrite fade mode set by FadeSetEnterScreen)
1115 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1118 void FadeSetLeaveScreen(void)
1120 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1123 void FadeSetFromType(int type)
1125 if (type & TYPE_ENTER_SCREEN)
1126 FadeSetEnterScreen();
1127 else if (type & TYPE_ENTER)
1129 else if (type & TYPE_LEAVE)
1133 void FadeSetDisabled(void)
1135 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1137 fading = fading_none;
1140 void FadeSkipNextFadeIn(void)
1142 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1145 void FadeSkipNextFadeOut(void)
1147 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1150 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1152 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1154 return (graphic == IMG_UNDEFINED ? NULL :
1155 graphic_info[graphic].bitmap != NULL || redefined ?
1156 graphic_info[graphic].bitmap :
1157 graphic_info[default_graphic].bitmap);
1160 static Bitmap *getBackgroundBitmap(int graphic)
1162 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1165 static Bitmap *getGlobalBorderBitmap(int graphic)
1167 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1170 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1173 (status == GAME_MODE_MAIN ||
1174 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1175 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1176 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1177 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1180 return getGlobalBorderBitmap(graphic);
1183 void SetWindowBackgroundImageIfDefined(int graphic)
1185 if (graphic_info[graphic].bitmap)
1186 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1189 void SetMainBackgroundImageIfDefined(int graphic)
1191 if (graphic_info[graphic].bitmap)
1192 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1195 void SetDoorBackgroundImageIfDefined(int graphic)
1197 if (graphic_info[graphic].bitmap)
1198 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1201 void SetWindowBackgroundImage(int graphic)
1203 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1206 void SetMainBackgroundImage(int graphic)
1208 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1211 void SetDoorBackgroundImage(int graphic)
1213 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1216 void SetPanelBackground(void)
1218 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1220 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1221 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1223 SetDoorBackgroundBitmap(bitmap_db_panel);
1226 void DrawBackground(int x, int y, int width, int height)
1228 // "drawto" might still point to playfield buffer here (hall of fame)
1229 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1231 if (IN_GFX_FIELD_FULL(x, y))
1232 redraw_mask |= REDRAW_FIELD;
1233 else if (IN_GFX_DOOR_1(x, y))
1234 redraw_mask |= REDRAW_DOOR_1;
1235 else if (IN_GFX_DOOR_2(x, y))
1236 redraw_mask |= REDRAW_DOOR_2;
1237 else if (IN_GFX_DOOR_3(x, y))
1238 redraw_mask |= REDRAW_DOOR_3;
1241 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1243 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1245 if (font->bitmap == NULL)
1248 DrawBackground(x, y, width, height);
1251 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1253 struct GraphicInfo *g = &graphic_info[graphic];
1255 if (g->bitmap == NULL)
1258 DrawBackground(x, y, width, height);
1261 static int game_status_last = -1;
1262 static Bitmap *global_border_bitmap_last = NULL;
1263 static Bitmap *global_border_bitmap = NULL;
1264 static int real_sx_last = -1, real_sy_last = -1;
1265 static int full_sxsize_last = -1, full_sysize_last = -1;
1266 static int dx_last = -1, dy_last = -1;
1267 static int dxsize_last = -1, dysize_last = -1;
1268 static int vx_last = -1, vy_last = -1;
1269 static int vxsize_last = -1, vysize_last = -1;
1270 static int ex_last = -1, ey_last = -1;
1271 static int exsize_last = -1, eysize_last = -1;
1273 boolean CheckIfGlobalBorderHasChanged(void)
1275 // if game status has not changed, global border has not changed either
1276 if (game_status == game_status_last)
1279 // determine and store new global border bitmap for current game status
1280 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1282 return (global_border_bitmap_last != global_border_bitmap);
1285 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1287 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1288 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1290 // if game status has not changed, nothing has to be redrawn
1291 if (game_status == game_status_last)
1294 // redraw if last screen was title screen
1295 if (game_status_last == GAME_MODE_TITLE)
1298 // redraw if global screen border has changed
1299 if (CheckIfGlobalBorderHasChanged())
1302 // redraw if position or size of playfield area has changed
1303 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1304 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1307 // redraw if position or size of door area has changed
1308 if (dx_last != DX || dy_last != DY ||
1309 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1312 // redraw if position or size of tape area has changed
1313 if (vx_last != VX || vy_last != VY ||
1314 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1317 // redraw if position or size of editor area has changed
1318 if (ex_last != EX || ey_last != EY ||
1319 exsize_last != EXSIZE || eysize_last != EYSIZE)
1326 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1329 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1331 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1334 void RedrawGlobalBorder(void)
1336 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1338 RedrawGlobalBorderFromBitmap(bitmap);
1340 redraw_mask = REDRAW_ALL;
1343 static void RedrawGlobalBorderIfNeeded(void)
1345 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1346 if (game_status == game_status_last)
1350 // copy current draw buffer to later copy back areas that have not changed
1351 if (game_status_last != GAME_MODE_TITLE)
1352 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1354 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1355 if (CheckIfGlobalBorderRedrawIsNeeded())
1358 // redraw global screen border (or clear, if defined to be empty)
1359 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1361 if (game_status == GAME_MODE_EDITOR)
1362 DrawSpecialEditorDoor();
1364 // copy previous playfield and door areas, if they are defined on both
1365 // previous and current screen and if they still have the same size
1367 if (real_sx_last != -1 && real_sy_last != -1 &&
1368 REAL_SX != -1 && REAL_SY != -1 &&
1369 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1370 BlitBitmap(bitmap_db_store_1, backbuffer,
1371 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1374 if (dx_last != -1 && dy_last != -1 &&
1375 DX != -1 && DY != -1 &&
1376 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1377 BlitBitmap(bitmap_db_store_1, backbuffer,
1378 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1380 if (game_status != GAME_MODE_EDITOR)
1382 if (vx_last != -1 && vy_last != -1 &&
1383 VX != -1 && VY != -1 &&
1384 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1385 BlitBitmap(bitmap_db_store_1, backbuffer,
1386 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1390 if (ex_last != -1 && ey_last != -1 &&
1391 EX != -1 && EY != -1 &&
1392 exsize_last == EXSIZE && eysize_last == EYSIZE)
1393 BlitBitmap(bitmap_db_store_1, backbuffer,
1394 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1397 redraw_mask = REDRAW_ALL;
1400 game_status_last = game_status;
1402 global_border_bitmap_last = global_border_bitmap;
1404 real_sx_last = REAL_SX;
1405 real_sy_last = REAL_SY;
1406 full_sxsize_last = FULL_SXSIZE;
1407 full_sysize_last = FULL_SYSIZE;
1410 dxsize_last = DXSIZE;
1411 dysize_last = DYSIZE;
1414 vxsize_last = VXSIZE;
1415 vysize_last = VYSIZE;
1418 exsize_last = EXSIZE;
1419 eysize_last = EYSIZE;
1422 void ClearField(void)
1424 RedrawGlobalBorderIfNeeded();
1426 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1427 // (when entering hall of fame after playing)
1428 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1430 // !!! maybe this should be done before clearing the background !!!
1431 if (game_status == GAME_MODE_PLAYING)
1433 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1434 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1438 SetDrawtoField(DRAW_TO_BACKBUFFER);
1442 void MarkTileDirty(int x, int y)
1444 redraw_mask |= REDRAW_FIELD;
1447 void SetBorderElement(void)
1451 BorderElement = EL_EMPTY;
1453 // the MM game engine does not use a visible border element
1454 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1457 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1459 for (x = 0; x < lev_fieldx; x++)
1461 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1462 BorderElement = EL_STEELWALL;
1464 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1470 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1471 int max_array_fieldx, int max_array_fieldy,
1472 short field[max_array_fieldx][max_array_fieldy],
1473 int max_fieldx, int max_fieldy)
1477 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1478 static int safety = 0;
1480 // check if starting field still has the desired content
1481 if (field[from_x][from_y] == fill_element)
1486 if (safety > max_fieldx * max_fieldy)
1487 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1489 old_element = field[from_x][from_y];
1490 field[from_x][from_y] = fill_element;
1492 for (i = 0; i < 4; i++)
1494 x = from_x + check[i][0];
1495 y = from_y + check[i][1];
1497 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1498 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1499 field, max_fieldx, max_fieldy);
1505 void FloodFillLevel(int from_x, int from_y, int fill_element,
1506 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1507 int max_fieldx, int max_fieldy)
1509 FloodFillLevelExt(from_x, from_y, fill_element,
1510 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1511 max_fieldx, max_fieldy);
1514 void SetRandomAnimationValue(int x, int y)
1516 gfx.anim_random_frame = GfxRandom[x][y];
1519 int getGraphicAnimationFrame(int graphic, int sync_frame)
1521 // animation synchronized with global frame counter, not move position
1522 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1523 sync_frame = FrameCounter;
1525 return getAnimationFrame(graphic_info[graphic].anim_frames,
1526 graphic_info[graphic].anim_delay,
1527 graphic_info[graphic].anim_mode,
1528 graphic_info[graphic].anim_start_frame,
1532 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1534 struct GraphicInfo *g = &graphic_info[graphic];
1535 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1537 if (tilesize == gfx.standard_tile_size)
1538 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1539 else if (tilesize == game.tile_size)
1540 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1542 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1545 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1546 boolean get_backside)
1548 struct GraphicInfo *g = &graphic_info[graphic];
1549 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1550 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1552 if (g->offset_y == 0) // frames are ordered horizontally
1554 int max_width = g->anim_frames_per_line * g->width;
1555 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1557 *x = pos % max_width;
1558 *y = src_y % g->height + pos / max_width * g->height;
1560 else if (g->offset_x == 0) // frames are ordered vertically
1562 int max_height = g->anim_frames_per_line * g->height;
1563 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1565 *x = src_x % g->width + pos / max_height * g->width;
1566 *y = pos % max_height;
1568 else // frames are ordered diagonally
1570 *x = src_x + frame * g->offset_x;
1571 *y = src_y + frame * g->offset_y;
1575 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1576 Bitmap **bitmap, int *x, int *y,
1577 boolean get_backside)
1579 struct GraphicInfo *g = &graphic_info[graphic];
1581 // if no graphics defined at all, use fallback graphics
1582 if (g->bitmaps == NULL)
1583 *g = graphic_info[IMG_CHAR_EXCLAM];
1585 // if no in-game graphics defined, always use standard graphic size
1586 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1587 tilesize = TILESIZE;
1589 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1590 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1592 *x = *x * tilesize / g->tile_size;
1593 *y = *y * tilesize / g->tile_size;
1596 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1597 Bitmap **bitmap, int *x, int *y)
1599 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1602 void getFixedGraphicSource(int graphic, int frame,
1603 Bitmap **bitmap, int *x, int *y)
1605 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1608 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1610 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1613 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1614 int *x, int *y, boolean get_backside)
1616 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1620 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1622 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1625 void DrawGraphic(int x, int y, int graphic, int frame)
1628 if (!IN_SCR_FIELD(x, y))
1630 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1631 printf("DrawGraphic(): This should never happen!\n");
1636 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1639 MarkTileDirty(x, y);
1642 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1645 if (!IN_SCR_FIELD(x, y))
1647 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1648 printf("DrawGraphic(): This should never happen!\n");
1653 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1655 MarkTileDirty(x, y);
1658 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1664 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1666 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1669 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1675 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1676 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1679 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1682 if (!IN_SCR_FIELD(x, y))
1684 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1685 printf("DrawGraphicThruMask(): This should never happen!\n");
1690 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1693 MarkTileDirty(x, y);
1696 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1699 if (!IN_SCR_FIELD(x, y))
1701 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1702 printf("DrawGraphicThruMask(): This should never happen!\n");
1707 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1709 MarkTileDirty(x, y);
1712 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1718 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1720 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1724 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1725 int graphic, int frame)
1730 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1732 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1736 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1738 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1740 MarkTileDirty(x / tilesize, y / tilesize);
1743 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1746 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1747 graphic, frame, tilesize);
1748 MarkTileDirty(x / tilesize, y / tilesize);
1751 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1757 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1758 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1761 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1762 int frame, int tilesize)
1767 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1768 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1771 void DrawMiniGraphic(int x, int y, int graphic)
1773 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1774 MarkTileDirty(x / 2, y / 2);
1777 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1782 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1783 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1786 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1787 int graphic, int frame,
1788 int cut_mode, int mask_mode)
1793 int width = TILEX, height = TILEY;
1796 if (dx || dy) // shifted graphic
1798 if (x < BX1) // object enters playfield from the left
1805 else if (x > BX2) // object enters playfield from the right
1811 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1817 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1819 else if (dx) // general horizontal movement
1820 MarkTileDirty(x + SIGN(dx), y);
1822 if (y < BY1) // object enters playfield from the top
1824 if (cut_mode == CUT_BELOW) // object completely above top border
1832 else if (y > BY2) // object enters playfield from the bottom
1838 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1844 else if (dy > 0 && cut_mode == CUT_ABOVE)
1846 if (y == BY2) // object completely above bottom border
1852 MarkTileDirty(x, y + 1);
1853 } // object leaves playfield to the bottom
1854 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1856 else if (dy) // general vertical movement
1857 MarkTileDirty(x, y + SIGN(dy));
1861 if (!IN_SCR_FIELD(x, y))
1863 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1864 printf("DrawGraphicShifted(): This should never happen!\n");
1869 width = width * TILESIZE_VAR / TILESIZE;
1870 height = height * TILESIZE_VAR / TILESIZE;
1871 cx = cx * TILESIZE_VAR / TILESIZE;
1872 cy = cy * TILESIZE_VAR / TILESIZE;
1873 dx = dx * TILESIZE_VAR / TILESIZE;
1874 dy = dy * TILESIZE_VAR / TILESIZE;
1876 if (width > 0 && height > 0)
1878 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1883 dst_x = FX + x * TILEX_VAR + dx;
1884 dst_y = FY + y * TILEY_VAR + dy;
1886 if (mask_mode == USE_MASKING)
1887 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1890 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1893 MarkTileDirty(x, y);
1897 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1898 int graphic, int frame,
1899 int cut_mode, int mask_mode)
1904 int width = TILEX_VAR, height = TILEY_VAR;
1907 int x2 = x + SIGN(dx);
1908 int y2 = y + SIGN(dy);
1910 // movement with two-tile animations must be sync'ed with movement position,
1911 // not with current GfxFrame (which can be higher when using slow movement)
1912 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1913 int anim_frames = graphic_info[graphic].anim_frames;
1915 // (we also need anim_delay here for movement animations with less frames)
1916 int anim_delay = graphic_info[graphic].anim_delay;
1917 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1919 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1920 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1922 // re-calculate animation frame for two-tile movement animation
1923 frame = getGraphicAnimationFrame(graphic, sync_frame);
1925 // check if movement start graphic inside screen area and should be drawn
1926 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1928 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1930 dst_x = FX + x1 * TILEX_VAR;
1931 dst_y = FY + y1 * TILEY_VAR;
1933 if (mask_mode == USE_MASKING)
1934 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1937 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1940 MarkTileDirty(x1, y1);
1943 // check if movement end graphic inside screen area and should be drawn
1944 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1946 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1948 dst_x = FX + x2 * TILEX_VAR;
1949 dst_y = FY + y2 * TILEY_VAR;
1951 if (mask_mode == USE_MASKING)
1952 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1955 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1958 MarkTileDirty(x2, y2);
1962 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1963 int graphic, int frame,
1964 int cut_mode, int mask_mode)
1968 DrawGraphic(x, y, graphic, frame);
1973 if (graphic_info[graphic].double_movement) // EM style movement images
1974 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1976 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1979 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1980 int graphic, int frame, int cut_mode)
1982 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1985 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1986 int cut_mode, int mask_mode)
1988 int lx = LEVELX(x), ly = LEVELY(y);
1992 if (IN_LEV_FIELD(lx, ly))
1994 SetRandomAnimationValue(lx, ly);
1996 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1997 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1999 // do not use double (EM style) movement graphic when not moving
2000 if (graphic_info[graphic].double_movement && !dx && !dy)
2002 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2003 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2006 else // border element
2008 graphic = el2img(element);
2009 frame = getGraphicAnimationFrame(graphic, -1);
2012 if (element == EL_EXPANDABLE_WALL)
2014 boolean left_stopped = FALSE, right_stopped = FALSE;
2016 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2017 left_stopped = TRUE;
2018 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2019 right_stopped = TRUE;
2021 if (left_stopped && right_stopped)
2023 else if (left_stopped)
2025 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2026 frame = graphic_info[graphic].anim_frames - 1;
2028 else if (right_stopped)
2030 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2031 frame = graphic_info[graphic].anim_frames - 1;
2036 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2037 else if (mask_mode == USE_MASKING)
2038 DrawGraphicThruMask(x, y, graphic, frame);
2040 DrawGraphic(x, y, graphic, frame);
2043 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2044 int cut_mode, int mask_mode)
2046 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2047 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2048 cut_mode, mask_mode);
2051 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2054 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2057 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2060 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2063 void DrawLevelElementThruMask(int x, int y, int element)
2065 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2068 void DrawLevelFieldThruMask(int x, int y)
2070 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2073 // !!! implementation of quicksand is totally broken !!!
2074 #define IS_CRUMBLED_TILE(x, y, e) \
2075 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2076 !IS_MOVING(x, y) || \
2077 (e) == EL_QUICKSAND_EMPTYING || \
2078 (e) == EL_QUICKSAND_FAST_EMPTYING))
2080 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2085 int width, height, cx, cy;
2086 int sx = SCREENX(x), sy = SCREENY(y);
2087 int crumbled_border_size = graphic_info[graphic].border_size;
2088 int crumbled_tile_size = graphic_info[graphic].tile_size;
2089 int crumbled_border_size_var =
2090 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2093 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2095 for (i = 1; i < 4; i++)
2097 int dxx = (i & 1 ? dx : 0);
2098 int dyy = (i & 2 ? dy : 0);
2101 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2104 // check if neighbour field is of same crumble type
2105 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2106 graphic_info[graphic].class ==
2107 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2109 // return if check prevents inner corner
2110 if (same == (dxx == dx && dyy == dy))
2114 // if we reach this point, we have an inner corner
2116 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2118 width = crumbled_border_size_var;
2119 height = crumbled_border_size_var;
2120 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2121 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2123 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2124 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2127 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2132 int width, height, bx, by, cx, cy;
2133 int sx = SCREENX(x), sy = SCREENY(y);
2134 int crumbled_border_size = graphic_info[graphic].border_size;
2135 int crumbled_tile_size = graphic_info[graphic].tile_size;
2136 int crumbled_border_size_var =
2137 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2138 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2141 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2143 // draw simple, sloppy, non-corner-accurate crumbled border
2145 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2146 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2147 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2148 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2150 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2151 FX + sx * TILEX_VAR + cx,
2152 FY + sy * TILEY_VAR + cy);
2154 // (remaining middle border part must be at least as big as corner part)
2155 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2156 crumbled_border_size_var >= TILESIZE_VAR / 3)
2159 // correct corners of crumbled border, if needed
2161 for (i = -1; i <= 1; i += 2)
2163 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2164 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2165 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2168 // check if neighbour field is of same crumble type
2169 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2170 graphic_info[graphic].class ==
2171 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2173 // no crumbled corner, but continued crumbled border
2175 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2176 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2177 int b1 = (i == 1 ? crumbled_border_size_var :
2178 TILESIZE_VAR - 2 * crumbled_border_size_var);
2180 width = crumbled_border_size_var;
2181 height = crumbled_border_size_var;
2183 if (dir == 1 || dir == 2)
2198 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2200 FX + sx * TILEX_VAR + cx,
2201 FY + sy * TILEY_VAR + cy);
2206 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2208 int sx = SCREENX(x), sy = SCREENY(y);
2211 static int xy[4][2] =
2219 if (!IN_LEV_FIELD(x, y))
2222 element = TILE_GFX_ELEMENT(x, y);
2224 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2226 if (!IN_SCR_FIELD(sx, sy))
2229 // crumble field borders towards direct neighbour fields
2230 for (i = 0; i < 4; i++)
2232 int xx = x + xy[i][0];
2233 int yy = y + xy[i][1];
2235 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2238 // check if neighbour field is of same crumble type
2239 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2240 graphic_info[graphic].class ==
2241 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2244 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2247 // crumble inner field corners towards corner neighbour fields
2248 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2249 graphic_info[graphic].anim_frames == 2)
2251 for (i = 0; i < 4; i++)
2253 int dx = (i & 1 ? +1 : -1);
2254 int dy = (i & 2 ? +1 : -1);
2256 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2260 MarkTileDirty(sx, sy);
2262 else // center field is not crumbled -- crumble neighbour fields
2264 // crumble field borders of direct neighbour fields
2265 for (i = 0; i < 4; i++)
2267 int xx = x + xy[i][0];
2268 int yy = y + xy[i][1];
2269 int sxx = sx + xy[i][0];
2270 int syy = sy + xy[i][1];
2272 if (!IN_LEV_FIELD(xx, yy) ||
2273 !IN_SCR_FIELD(sxx, syy))
2276 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2279 element = TILE_GFX_ELEMENT(xx, yy);
2281 if (!IS_CRUMBLED_TILE(xx, yy, element))
2284 graphic = el_act2crm(element, ACTION_DEFAULT);
2286 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2288 MarkTileDirty(sxx, syy);
2291 // crumble inner field corners of corner neighbour fields
2292 for (i = 0; i < 4; i++)
2294 int dx = (i & 1 ? +1 : -1);
2295 int dy = (i & 2 ? +1 : -1);
2301 if (!IN_LEV_FIELD(xx, yy) ||
2302 !IN_SCR_FIELD(sxx, syy))
2305 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2308 element = TILE_GFX_ELEMENT(xx, yy);
2310 if (!IS_CRUMBLED_TILE(xx, yy, element))
2313 graphic = el_act2crm(element, ACTION_DEFAULT);
2315 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2316 graphic_info[graphic].anim_frames == 2)
2317 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2319 MarkTileDirty(sxx, syy);
2324 void DrawLevelFieldCrumbled(int x, int y)
2328 if (!IN_LEV_FIELD(x, y))
2331 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2332 GfxElement[x][y] != EL_UNDEFINED &&
2333 GFX_CRUMBLED(GfxElement[x][y]))
2335 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2340 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2342 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2345 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2348 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2349 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2350 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2351 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2352 int sx = SCREENX(x), sy = SCREENY(y);
2354 DrawGraphic(sx, sy, graphic1, frame1);
2355 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2358 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2360 int sx = SCREENX(x), sy = SCREENY(y);
2361 static int xy[4][2] =
2370 // crumble direct neighbour fields (required for field borders)
2371 for (i = 0; i < 4; i++)
2373 int xx = x + xy[i][0];
2374 int yy = y + xy[i][1];
2375 int sxx = sx + xy[i][0];
2376 int syy = sy + xy[i][1];
2378 if (!IN_LEV_FIELD(xx, yy) ||
2379 !IN_SCR_FIELD(sxx, syy) ||
2380 !GFX_CRUMBLED(Feld[xx][yy]) ||
2384 DrawLevelField(xx, yy);
2387 // crumble corner neighbour fields (required for inner field corners)
2388 for (i = 0; i < 4; i++)
2390 int dx = (i & 1 ? +1 : -1);
2391 int dy = (i & 2 ? +1 : -1);
2397 if (!IN_LEV_FIELD(xx, yy) ||
2398 !IN_SCR_FIELD(sxx, syy) ||
2399 !GFX_CRUMBLED(Feld[xx][yy]) ||
2403 int element = TILE_GFX_ELEMENT(xx, yy);
2404 int graphic = el_act2crm(element, ACTION_DEFAULT);
2406 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2407 graphic_info[graphic].anim_frames == 2)
2408 DrawLevelField(xx, yy);
2412 static int getBorderElement(int x, int y)
2416 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2417 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2418 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2419 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2420 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2421 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2422 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2424 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2425 int steel_position = (x == -1 && y == -1 ? 0 :
2426 x == lev_fieldx && y == -1 ? 1 :
2427 x == -1 && y == lev_fieldy ? 2 :
2428 x == lev_fieldx && y == lev_fieldy ? 3 :
2429 x == -1 || x == lev_fieldx ? 4 :
2430 y == -1 || y == lev_fieldy ? 5 : 6);
2432 return border[steel_position][steel_type];
2435 void DrawScreenElement(int x, int y, int element)
2437 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2438 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2441 void DrawLevelElement(int x, int y, int element)
2443 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2444 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2447 void DrawScreenField(int x, int y)
2449 int lx = LEVELX(x), ly = LEVELY(y);
2450 int element, content;
2452 if (!IN_LEV_FIELD(lx, ly))
2454 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2457 element = getBorderElement(lx, ly);
2459 DrawScreenElement(x, y, element);
2464 element = Feld[lx][ly];
2465 content = Store[lx][ly];
2467 if (IS_MOVING(lx, ly))
2469 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2470 boolean cut_mode = NO_CUTTING;
2472 if (element == EL_QUICKSAND_EMPTYING ||
2473 element == EL_QUICKSAND_FAST_EMPTYING ||
2474 element == EL_MAGIC_WALL_EMPTYING ||
2475 element == EL_BD_MAGIC_WALL_EMPTYING ||
2476 element == EL_DC_MAGIC_WALL_EMPTYING ||
2477 element == EL_AMOEBA_DROPPING)
2478 cut_mode = CUT_ABOVE;
2479 else if (element == EL_QUICKSAND_FILLING ||
2480 element == EL_QUICKSAND_FAST_FILLING ||
2481 element == EL_MAGIC_WALL_FILLING ||
2482 element == EL_BD_MAGIC_WALL_FILLING ||
2483 element == EL_DC_MAGIC_WALL_FILLING)
2484 cut_mode = CUT_BELOW;
2486 if (cut_mode == CUT_ABOVE)
2487 DrawScreenElement(x, y, element);
2489 DrawScreenElement(x, y, EL_EMPTY);
2492 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2493 else if (cut_mode == NO_CUTTING)
2494 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2497 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2499 if (cut_mode == CUT_BELOW &&
2500 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2501 DrawLevelElement(lx, ly + 1, element);
2504 if (content == EL_ACID)
2506 int dir = MovDir[lx][ly];
2507 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2508 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2510 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2512 // prevent target field from being drawn again (but without masking)
2513 // (this would happen if target field is scanned after moving element)
2514 Stop[newlx][newly] = TRUE;
2517 else if (IS_BLOCKED(lx, ly))
2522 boolean cut_mode = NO_CUTTING;
2523 int element_old, content_old;
2525 Blocked2Moving(lx, ly, &oldx, &oldy);
2528 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2529 MovDir[oldx][oldy] == MV_RIGHT);
2531 element_old = Feld[oldx][oldy];
2532 content_old = Store[oldx][oldy];
2534 if (element_old == EL_QUICKSAND_EMPTYING ||
2535 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2536 element_old == EL_MAGIC_WALL_EMPTYING ||
2537 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2538 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2539 element_old == EL_AMOEBA_DROPPING)
2540 cut_mode = CUT_ABOVE;
2542 DrawScreenElement(x, y, EL_EMPTY);
2545 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2547 else if (cut_mode == NO_CUTTING)
2548 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2551 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2554 else if (IS_DRAWABLE(element))
2555 DrawScreenElement(x, y, element);
2557 DrawScreenElement(x, y, EL_EMPTY);
2560 void DrawLevelField(int x, int y)
2562 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2563 DrawScreenField(SCREENX(x), SCREENY(y));
2564 else if (IS_MOVING(x, y))
2568 Moving2Blocked(x, y, &newx, &newy);
2569 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2570 DrawScreenField(SCREENX(newx), SCREENY(newy));
2572 else if (IS_BLOCKED(x, y))
2576 Blocked2Moving(x, y, &oldx, &oldy);
2577 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2578 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2582 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2583 int (*el2img_function)(int), boolean masked,
2584 int element_bits_draw)
2586 int element_base = map_mm_wall_element(element);
2587 int element_bits = (IS_DF_WALL(element) ?
2588 element - EL_DF_WALL_START :
2589 IS_MM_WALL(element) ?
2590 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2591 int graphic = el2img_function(element_base);
2592 int tilesize_draw = tilesize / 2;
2597 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2599 for (i = 0; i < 4; i++)
2601 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2602 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2604 if (!(element_bits_draw & (1 << i)))
2607 if (element_bits & (1 << i))
2610 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2611 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2613 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2614 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2619 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2620 tilesize_draw, tilesize_draw);
2625 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2626 boolean masked, int element_bits_draw)
2628 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2629 element, tilesize, el2edimg, masked, element_bits_draw);
2632 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2633 int (*el2img_function)(int))
2635 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2639 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2642 if (IS_MM_WALL(element))
2644 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2645 element, tilesize, el2edimg, masked, 0x000f);
2649 int graphic = el2edimg(element);
2652 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2654 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2658 void DrawSizedElement(int x, int y, int element, int tilesize)
2660 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2663 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2665 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2668 void DrawMiniElement(int x, int y, int element)
2672 graphic = el2edimg(element);
2673 DrawMiniGraphic(x, y, graphic);
2676 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2679 int x = sx + scroll_x, y = sy + scroll_y;
2681 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2682 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2683 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2684 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2686 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2689 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2691 int x = sx + scroll_x, y = sy + scroll_y;
2693 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2694 DrawMiniElement(sx, sy, EL_EMPTY);
2695 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2696 DrawMiniElement(sx, sy, Feld[x][y]);
2698 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2701 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2702 int x, int y, int xsize, int ysize,
2703 int tile_width, int tile_height)
2707 int dst_x = startx + x * tile_width;
2708 int dst_y = starty + y * tile_height;
2709 int width = graphic_info[graphic].width;
2710 int height = graphic_info[graphic].height;
2711 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2712 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2713 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2714 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2715 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2716 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2717 boolean draw_masked = graphic_info[graphic].draw_masked;
2719 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2721 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2723 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2727 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2728 inner_sx + (x - 1) * tile_width % inner_width);
2729 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2730 inner_sy + (y - 1) * tile_height % inner_height);
2733 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2736 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2740 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2741 int x, int y, int xsize, int ysize,
2744 int font_width = getFontWidth(font_nr);
2745 int font_height = getFontHeight(font_nr);
2747 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2748 font_width, font_height);
2751 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2753 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2754 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2755 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2756 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2757 boolean no_delay = (tape.warp_forward);
2758 unsigned int anim_delay = 0;
2759 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2760 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2761 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2762 int font_width = getFontWidth(font_nr);
2763 int font_height = getFontHeight(font_nr);
2764 int max_xsize = level.envelope[envelope_nr].xsize;
2765 int max_ysize = level.envelope[envelope_nr].ysize;
2766 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2767 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2768 int xend = max_xsize;
2769 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2770 int xstep = (xstart < xend ? 1 : 0);
2771 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2773 int end = MAX(xend - xstart, yend - ystart);
2776 for (i = start; i <= end; i++)
2778 int last_frame = end; // last frame of this "for" loop
2779 int x = xstart + i * xstep;
2780 int y = ystart + i * ystep;
2781 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2782 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2783 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2784 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2787 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2789 BlitScreenToBitmap(backbuffer);
2791 SetDrawtoField(DRAW_TO_BACKBUFFER);
2793 for (yy = 0; yy < ysize; yy++)
2794 for (xx = 0; xx < xsize; xx++)
2795 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2797 DrawTextBuffer(sx + font_width, sy + font_height,
2798 level.envelope[envelope_nr].text, font_nr, max_xsize,
2799 xsize - 2, ysize - 2, 0, mask_mode,
2800 level.envelope[envelope_nr].autowrap,
2801 level.envelope[envelope_nr].centered, FALSE);
2803 redraw_mask |= REDRAW_FIELD;
2806 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2809 ClearAutoRepeatKeyEvents();
2812 void ShowEnvelope(int envelope_nr)
2814 int element = EL_ENVELOPE_1 + envelope_nr;
2815 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2816 int sound_opening = element_info[element].sound[ACTION_OPENING];
2817 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2818 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2819 boolean no_delay = (tape.warp_forward);
2820 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2821 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2822 int anim_mode = graphic_info[graphic].anim_mode;
2823 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2824 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2826 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2828 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2830 if (anim_mode == ANIM_DEFAULT)
2831 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2833 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2836 Delay(wait_delay_value);
2838 WaitForEventToContinue();
2840 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2842 if (anim_mode != ANIM_NONE)
2843 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2845 if (anim_mode == ANIM_DEFAULT)
2846 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2848 game.envelope_active = FALSE;
2850 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2852 redraw_mask |= REDRAW_FIELD;
2856 static void setRequestBasePosition(int *x, int *y)
2858 int sx_base, sy_base;
2860 if (request.x != -1)
2861 sx_base = request.x;
2862 else if (request.align == ALIGN_LEFT)
2864 else if (request.align == ALIGN_RIGHT)
2865 sx_base = SX + SXSIZE;
2867 sx_base = SX + SXSIZE / 2;
2869 if (request.y != -1)
2870 sy_base = request.y;
2871 else if (request.valign == VALIGN_TOP)
2873 else if (request.valign == VALIGN_BOTTOM)
2874 sy_base = SY + SYSIZE;
2876 sy_base = SY + SYSIZE / 2;
2882 static void setRequestPositionExt(int *x, int *y, int width, int height,
2883 boolean add_border_size)
2885 int border_size = request.border_size;
2886 int sx_base, sy_base;
2889 setRequestBasePosition(&sx_base, &sy_base);
2891 if (request.align == ALIGN_LEFT)
2893 else if (request.align == ALIGN_RIGHT)
2894 sx = sx_base - width;
2896 sx = sx_base - width / 2;
2898 if (request.valign == VALIGN_TOP)
2900 else if (request.valign == VALIGN_BOTTOM)
2901 sy = sy_base - height;
2903 sy = sy_base - height / 2;
2905 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2906 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2908 if (add_border_size)
2918 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2920 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2923 static void DrawEnvelopeRequest(char *text)
2925 char *text_final = text;
2926 char *text_door_style = NULL;
2927 int graphic = IMG_BACKGROUND_REQUEST;
2928 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2929 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2930 int font_nr = FONT_REQUEST;
2931 int font_width = getFontWidth(font_nr);
2932 int font_height = getFontHeight(font_nr);
2933 int border_size = request.border_size;
2934 int line_spacing = request.line_spacing;
2935 int line_height = font_height + line_spacing;
2936 int max_text_width = request.width - 2 * border_size;
2937 int max_text_height = request.height - 2 * border_size;
2938 int line_length = max_text_width / font_width;
2939 int max_lines = max_text_height / line_height;
2940 int text_width = line_length * font_width;
2941 int width = request.width;
2942 int height = request.height;
2943 int tile_size = MAX(request.step_offset, 1);
2944 int x_steps = width / tile_size;
2945 int y_steps = height / tile_size;
2946 int sx_offset = border_size;
2947 int sy_offset = border_size;
2951 if (request.centered)
2952 sx_offset = (request.width - text_width) / 2;
2954 if (request.wrap_single_words && !request.autowrap)
2956 char *src_text_ptr, *dst_text_ptr;
2958 text_door_style = checked_malloc(2 * strlen(text) + 1);
2960 src_text_ptr = text;
2961 dst_text_ptr = text_door_style;
2963 while (*src_text_ptr)
2965 if (*src_text_ptr == ' ' ||
2966 *src_text_ptr == '?' ||
2967 *src_text_ptr == '!')
2968 *dst_text_ptr++ = '\n';
2970 if (*src_text_ptr != ' ')
2971 *dst_text_ptr++ = *src_text_ptr;
2976 *dst_text_ptr = '\0';
2978 text_final = text_door_style;
2981 setRequestPosition(&sx, &sy, FALSE);
2983 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2985 for (y = 0; y < y_steps; y++)
2986 for (x = 0; x < x_steps; x++)
2987 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2988 x, y, x_steps, y_steps,
2989 tile_size, tile_size);
2991 // force DOOR font inside door area
2992 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2994 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2995 line_length, -1, max_lines, line_spacing, mask_mode,
2996 request.autowrap, request.centered, FALSE);
3000 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3001 RedrawGadget(tool_gadget[i]);
3003 // store readily prepared envelope request for later use when animating
3004 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3006 if (text_door_style)
3007 free(text_door_style);
3010 static void AnimateEnvelopeRequest(int anim_mode, int action)
3012 int graphic = IMG_BACKGROUND_REQUEST;
3013 boolean draw_masked = graphic_info[graphic].draw_masked;
3014 int delay_value_normal = request.step_delay;
3015 int delay_value_fast = delay_value_normal / 2;
3016 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3017 boolean no_delay = (tape.warp_forward);
3018 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3019 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3020 unsigned int anim_delay = 0;
3022 int tile_size = MAX(request.step_offset, 1);
3023 int max_xsize = request.width / tile_size;
3024 int max_ysize = request.height / tile_size;
3025 int max_xsize_inner = max_xsize - 2;
3026 int max_ysize_inner = max_ysize - 2;
3028 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3029 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3030 int xend = max_xsize_inner;
3031 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3032 int xstep = (xstart < xend ? 1 : 0);
3033 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3035 int end = MAX(xend - xstart, yend - ystart);
3038 if (setup.quick_doors)
3045 for (i = start; i <= end; i++)
3047 int last_frame = end; // last frame of this "for" loop
3048 int x = xstart + i * xstep;
3049 int y = ystart + i * ystep;
3050 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3051 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3052 int xsize_size_left = (xsize - 1) * tile_size;
3053 int ysize_size_top = (ysize - 1) * tile_size;
3054 int max_xsize_pos = (max_xsize - 1) * tile_size;
3055 int max_ysize_pos = (max_ysize - 1) * tile_size;
3056 int width = xsize * tile_size;
3057 int height = ysize * tile_size;
3062 setRequestPosition(&src_x, &src_y, FALSE);
3063 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3065 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3067 for (yy = 0; yy < 2; yy++)
3069 for (xx = 0; xx < 2; xx++)
3071 int src_xx = src_x + xx * max_xsize_pos;
3072 int src_yy = src_y + yy * max_ysize_pos;
3073 int dst_xx = dst_x + xx * xsize_size_left;
3074 int dst_yy = dst_y + yy * ysize_size_top;
3075 int xx_size = (xx ? tile_size : xsize_size_left);
3076 int yy_size = (yy ? tile_size : ysize_size_top);
3079 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3080 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3082 BlitBitmap(bitmap_db_store_2, backbuffer,
3083 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3087 redraw_mask |= REDRAW_FIELD;
3091 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3094 ClearAutoRepeatKeyEvents();
3097 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3099 int graphic = IMG_BACKGROUND_REQUEST;
3100 int sound_opening = SND_REQUEST_OPENING;
3101 int sound_closing = SND_REQUEST_CLOSING;
3102 int anim_mode_1 = request.anim_mode; // (higher priority)
3103 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3104 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3105 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3106 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3108 if (game_status == GAME_MODE_PLAYING)
3109 BlitScreenToBitmap(backbuffer);
3111 SetDrawtoField(DRAW_TO_BACKBUFFER);
3113 // SetDrawBackgroundMask(REDRAW_NONE);
3115 if (action == ACTION_OPENING)
3117 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3119 if (req_state & REQ_ASK)
3121 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3122 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3124 else if (req_state & REQ_CONFIRM)
3126 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3128 else if (req_state & REQ_PLAYER)
3130 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3131 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3132 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3136 DrawEnvelopeRequest(text);
3139 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3141 if (action == ACTION_OPENING)
3143 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3145 if (anim_mode == ANIM_DEFAULT)
3146 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3148 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3152 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3154 if (anim_mode != ANIM_NONE)
3155 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3157 if (anim_mode == ANIM_DEFAULT)
3158 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3161 game.envelope_active = FALSE;
3163 if (action == ACTION_CLOSING)
3164 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3166 // SetDrawBackgroundMask(last_draw_background_mask);
3168 redraw_mask |= REDRAW_FIELD;
3172 if (action == ACTION_CLOSING &&
3173 game_status == GAME_MODE_PLAYING &&
3174 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3175 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3178 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3180 if (IS_MM_WALL(element))
3182 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3188 int graphic = el2preimg(element);
3190 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3191 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3196 void DrawLevel(int draw_background_mask)
3200 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3201 SetDrawBackgroundMask(draw_background_mask);
3205 for (x = BX1; x <= BX2; x++)
3206 for (y = BY1; y <= BY2; y++)
3207 DrawScreenField(x, y);
3209 redraw_mask |= REDRAW_FIELD;
3212 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3217 for (x = 0; x < size_x; x++)
3218 for (y = 0; y < size_y; y++)
3219 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3221 redraw_mask |= REDRAW_FIELD;
3224 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3228 for (x = 0; x < size_x; x++)
3229 for (y = 0; y < size_y; y++)
3230 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3232 redraw_mask |= REDRAW_FIELD;
3235 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3237 boolean show_level_border = (BorderElement != EL_EMPTY);
3238 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3239 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3240 int tile_size = preview.tile_size;
3241 int preview_width = preview.xsize * tile_size;
3242 int preview_height = preview.ysize * tile_size;
3243 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3244 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3245 int real_preview_width = real_preview_xsize * tile_size;
3246 int real_preview_height = real_preview_ysize * tile_size;
3247 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3248 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3251 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3254 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3256 dst_x += (preview_width - real_preview_width) / 2;
3257 dst_y += (preview_height - real_preview_height) / 2;
3259 for (x = 0; x < real_preview_xsize; x++)
3261 for (y = 0; y < real_preview_ysize; y++)
3263 int lx = from_x + x + (show_level_border ? -1 : 0);
3264 int ly = from_y + y + (show_level_border ? -1 : 0);
3265 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3266 getBorderElement(lx, ly));
3268 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3269 element, tile_size);
3273 redraw_mask |= REDRAW_FIELD;
3276 #define MICROLABEL_EMPTY 0
3277 #define MICROLABEL_LEVEL_NAME 1
3278 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3279 #define MICROLABEL_LEVEL_AUTHOR 3
3280 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3281 #define MICROLABEL_IMPORTED_FROM 5
3282 #define MICROLABEL_IMPORTED_BY_HEAD 6
3283 #define MICROLABEL_IMPORTED_BY 7
3285 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3287 int max_text_width = SXSIZE;
3288 int font_width = getFontWidth(font_nr);
3290 if (pos->align == ALIGN_CENTER)
3291 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3292 else if (pos->align == ALIGN_RIGHT)
3293 max_text_width = pos->x;
3295 max_text_width = SXSIZE - pos->x;
3297 return max_text_width / font_width;
3300 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3302 char label_text[MAX_OUTPUT_LINESIZE + 1];
3303 int max_len_label_text;
3304 int font_nr = pos->font;
3307 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3310 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3311 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3312 mode == MICROLABEL_IMPORTED_BY_HEAD)
3313 font_nr = pos->font_alt;
3315 max_len_label_text = getMaxTextLength(pos, font_nr);
3317 if (pos->size != -1)
3318 max_len_label_text = pos->size;
3320 for (i = 0; i < max_len_label_text; i++)
3321 label_text[i] = ' ';
3322 label_text[max_len_label_text] = '\0';
3324 if (strlen(label_text) > 0)
3325 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3328 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3329 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3330 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3331 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3332 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3333 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3334 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3335 max_len_label_text);
3336 label_text[max_len_label_text] = '\0';
3338 if (strlen(label_text) > 0)
3339 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3341 redraw_mask |= REDRAW_FIELD;
3344 static void DrawPreviewLevelLabel(int mode)
3346 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3349 static void DrawPreviewLevelInfo(int mode)
3351 if (mode == MICROLABEL_LEVEL_NAME)
3352 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3353 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3354 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3357 static void DrawPreviewLevelExt(boolean restart)
3359 static unsigned int scroll_delay = 0;
3360 static unsigned int label_delay = 0;
3361 static int from_x, from_y, scroll_direction;
3362 static int label_state, label_counter;
3363 unsigned int scroll_delay_value = preview.step_delay;
3364 boolean show_level_border = (BorderElement != EL_EMPTY);
3365 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3366 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3373 if (preview.anim_mode == ANIM_CENTERED)
3375 if (level_xsize > preview.xsize)
3376 from_x = (level_xsize - preview.xsize) / 2;
3377 if (level_ysize > preview.ysize)
3378 from_y = (level_ysize - preview.ysize) / 2;
3381 from_x += preview.xoffset;
3382 from_y += preview.yoffset;
3384 scroll_direction = MV_RIGHT;
3388 DrawPreviewLevelPlayfield(from_x, from_y);
3389 DrawPreviewLevelLabel(label_state);
3391 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3392 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3394 // initialize delay counters
3395 DelayReached(&scroll_delay, 0);
3396 DelayReached(&label_delay, 0);
3398 if (leveldir_current->name)
3400 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3401 char label_text[MAX_OUTPUT_LINESIZE + 1];
3402 int font_nr = pos->font;
3403 int max_len_label_text = getMaxTextLength(pos, font_nr);
3405 if (pos->size != -1)
3406 max_len_label_text = pos->size;
3408 strncpy(label_text, leveldir_current->name, max_len_label_text);
3409 label_text[max_len_label_text] = '\0';
3411 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3412 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3418 // scroll preview level, if needed
3419 if (preview.anim_mode != ANIM_NONE &&
3420 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3421 DelayReached(&scroll_delay, scroll_delay_value))
3423 switch (scroll_direction)
3428 from_x -= preview.step_offset;
3429 from_x = (from_x < 0 ? 0 : from_x);
3432 scroll_direction = MV_UP;
3436 if (from_x < level_xsize - preview.xsize)
3438 from_x += preview.step_offset;
3439 from_x = (from_x > level_xsize - preview.xsize ?
3440 level_xsize - preview.xsize : from_x);
3443 scroll_direction = MV_DOWN;
3449 from_y -= preview.step_offset;
3450 from_y = (from_y < 0 ? 0 : from_y);
3453 scroll_direction = MV_RIGHT;
3457 if (from_y < level_ysize - preview.ysize)
3459 from_y += preview.step_offset;
3460 from_y = (from_y > level_ysize - preview.ysize ?
3461 level_ysize - preview.ysize : from_y);
3464 scroll_direction = MV_LEFT;
3471 DrawPreviewLevelPlayfield(from_x, from_y);
3474 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3475 // redraw micro level label, if needed
3476 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3477 !strEqual(level.author, ANONYMOUS_NAME) &&
3478 !strEqual(level.author, leveldir_current->name) &&
3479 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3481 int max_label_counter = 23;
3483 if (leveldir_current->imported_from != NULL &&
3484 strlen(leveldir_current->imported_from) > 0)
3485 max_label_counter += 14;
3486 if (leveldir_current->imported_by != NULL &&
3487 strlen(leveldir_current->imported_by) > 0)
3488 max_label_counter += 14;
3490 label_counter = (label_counter + 1) % max_label_counter;
3491 label_state = (label_counter >= 0 && label_counter <= 7 ?
3492 MICROLABEL_LEVEL_NAME :
3493 label_counter >= 9 && label_counter <= 12 ?
3494 MICROLABEL_LEVEL_AUTHOR_HEAD :
3495 label_counter >= 14 && label_counter <= 21 ?
3496 MICROLABEL_LEVEL_AUTHOR :
3497 label_counter >= 23 && label_counter <= 26 ?
3498 MICROLABEL_IMPORTED_FROM_HEAD :
3499 label_counter >= 28 && label_counter <= 35 ?
3500 MICROLABEL_IMPORTED_FROM :
3501 label_counter >= 37 && label_counter <= 40 ?
3502 MICROLABEL_IMPORTED_BY_HEAD :
3503 label_counter >= 42 && label_counter <= 49 ?
3504 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3506 if (leveldir_current->imported_from == NULL &&
3507 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3508 label_state == MICROLABEL_IMPORTED_FROM))
3509 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3510 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3512 DrawPreviewLevelLabel(label_state);
3516 static void DrawPreviewPlayers(void)
3518 if (game_status != GAME_MODE_MAIN)
3521 if (!network.enabled && !setup.team_mode)
3524 boolean player_found[MAX_PLAYERS];
3525 int num_players = 0;
3528 for (i = 0; i < MAX_PLAYERS; i++)
3529 player_found[i] = FALSE;
3531 // check which players can be found in the level (simple approach)
3532 for (x = 0; x < lev_fieldx; x++)
3534 for (y = 0; y < lev_fieldy; y++)
3536 int element = level.field[x][y];
3538 if (ELEM_IS_PLAYER(element))
3540 int player_nr = GET_PLAYER_NR(element);
3542 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3544 if (!player_found[player_nr])
3547 player_found[player_nr] = TRUE;
3552 struct TextPosInfo *pos = &menu.main.preview_players;
3553 int tile_size = pos->tile_size;
3554 int border_size = pos->border_size;
3555 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3556 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3557 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3558 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3559 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3560 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3561 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3562 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3563 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3564 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3565 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3566 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3568 // clear area in which the players will be drawn
3569 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3570 max_players_width, max_players_height);
3572 // only draw players if level is suited for team mode
3573 if (num_players < 2)
3576 // draw all players that were found in the level
3577 for (i = 0; i < MAX_PLAYERS; i++)
3579 if (player_found[i])
3581 int graphic = el2img(EL_PLAYER_1 + i);
3583 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3585 xpos += player_xoffset;
3586 ypos += player_yoffset;
3591 void DrawPreviewLevelInitial(void)
3593 DrawPreviewLevelExt(TRUE);
3594 DrawPreviewPlayers();
3597 void DrawPreviewLevelAnimation(void)
3599 DrawPreviewLevelExt(FALSE);
3602 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3603 int border_size, int font_nr)
3605 int graphic = el2img(EL_PLAYER_1 + player_nr);
3606 int font_height = getFontHeight(font_nr);
3607 int player_height = MAX(tile_size, font_height);
3608 int xoffset_text = tile_size + border_size;
3609 int yoffset_text = (player_height - font_height) / 2;
3610 int yoffset_graphic = (player_height - tile_size) / 2;
3611 char *player_name = getNetworkPlayerName(player_nr + 1);
3613 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3615 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3618 static void DrawNetworkPlayersExt(boolean force)
3620 if (game_status != GAME_MODE_MAIN)
3623 if (!network.connected && !force)
3626 int num_players = 0;
3629 for (i = 0; i < MAX_PLAYERS; i++)
3630 if (stored_player[i].connected_network)
3633 struct TextPosInfo *pos = &menu.main.network_players;
3634 int tile_size = pos->tile_size;
3635 int border_size = pos->border_size;
3636 int xoffset_text = tile_size + border_size;
3637 int font_nr = pos->font;
3638 int font_width = getFontWidth(font_nr);
3639 int font_height = getFontHeight(font_nr);
3640 int player_height = MAX(tile_size, font_height);
3641 int player_yoffset = player_height + border_size;
3642 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3643 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3644 int all_players_height = num_players * player_yoffset - border_size;
3645 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3646 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3647 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3649 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3650 max_players_width, max_players_height);
3652 // first draw local network player ...
3653 for (i = 0; i < MAX_PLAYERS; i++)
3655 if (stored_player[i].connected_network &&
3656 stored_player[i].connected_locally)
3658 char *player_name = getNetworkPlayerName(i + 1);
3659 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3660 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3662 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3664 ypos += player_yoffset;
3668 // ... then draw all other network players
3669 for (i = 0; i < MAX_PLAYERS; i++)
3671 if (stored_player[i].connected_network &&
3672 !stored_player[i].connected_locally)
3674 char *player_name = getNetworkPlayerName(i + 1);
3675 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3676 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3678 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3680 ypos += player_yoffset;
3685 void DrawNetworkPlayers(void)
3687 DrawNetworkPlayersExt(FALSE);
3690 void ClearNetworkPlayers(void)
3692 DrawNetworkPlayersExt(TRUE);
3695 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3696 int graphic, int sync_frame,
3699 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3701 if (mask_mode == USE_MASKING)
3702 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3704 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3707 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3708 int graphic, int sync_frame, int mask_mode)
3710 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3712 if (mask_mode == USE_MASKING)
3713 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3715 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3718 static void DrawGraphicAnimation(int x, int y, int graphic)
3720 int lx = LEVELX(x), ly = LEVELY(y);
3722 if (!IN_SCR_FIELD(x, y))
3725 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3726 graphic, GfxFrame[lx][ly], NO_MASKING);
3728 MarkTileDirty(x, y);
3731 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3733 int lx = LEVELX(x), ly = LEVELY(y);
3735 if (!IN_SCR_FIELD(x, y))
3738 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3739 graphic, GfxFrame[lx][ly], NO_MASKING);
3740 MarkTileDirty(x, y);
3743 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3745 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3748 void DrawLevelElementAnimation(int x, int y, int element)
3750 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3752 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3755 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3757 int sx = SCREENX(x), sy = SCREENY(y);
3759 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3762 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3765 DrawGraphicAnimation(sx, sy, graphic);
3768 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3769 DrawLevelFieldCrumbled(x, y);
3771 if (GFX_CRUMBLED(Feld[x][y]))
3772 DrawLevelFieldCrumbled(x, y);
3776 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3778 int sx = SCREENX(x), sy = SCREENY(y);
3781 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3784 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3786 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3789 DrawGraphicAnimation(sx, sy, graphic);
3791 if (GFX_CRUMBLED(element))
3792 DrawLevelFieldCrumbled(x, y);
3795 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3797 if (player->use_murphy)
3799 // this works only because currently only one player can be "murphy" ...
3800 static int last_horizontal_dir = MV_LEFT;
3801 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3803 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3804 last_horizontal_dir = move_dir;
3806 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3808 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3810 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3816 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3819 static boolean equalGraphics(int graphic1, int graphic2)
3821 struct GraphicInfo *g1 = &graphic_info[graphic1];
3822 struct GraphicInfo *g2 = &graphic_info[graphic2];
3824 return (g1->bitmap == g2->bitmap &&
3825 g1->src_x == g2->src_x &&
3826 g1->src_y == g2->src_y &&
3827 g1->anim_frames == g2->anim_frames &&
3828 g1->anim_delay == g2->anim_delay &&
3829 g1->anim_mode == g2->anim_mode);
3832 void DrawAllPlayers(void)
3836 for (i = 0; i < MAX_PLAYERS; i++)
3837 if (stored_player[i].active)
3838 DrawPlayer(&stored_player[i]);
3841 void DrawPlayerField(int x, int y)
3843 if (!IS_PLAYER(x, y))
3846 DrawPlayer(PLAYERINFO(x, y));
3849 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3851 void DrawPlayer(struct PlayerInfo *player)
3853 int jx = player->jx;
3854 int jy = player->jy;
3855 int move_dir = player->MovDir;
3856 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3857 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3858 int last_jx = (player->is_moving ? jx - dx : jx);
3859 int last_jy = (player->is_moving ? jy - dy : jy);
3860 int next_jx = jx + dx;
3861 int next_jy = jy + dy;
3862 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3863 boolean player_is_opaque = FALSE;
3864 int sx = SCREENX(jx), sy = SCREENY(jy);
3865 int sxx = 0, syy = 0;
3866 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3868 int action = ACTION_DEFAULT;
3869 int last_player_graphic = getPlayerGraphic(player, move_dir);
3870 int last_player_frame = player->Frame;
3873 // GfxElement[][] is set to the element the player is digging or collecting;
3874 // remove also for off-screen player if the player is not moving anymore
3875 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3876 GfxElement[jx][jy] = EL_UNDEFINED;
3878 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3882 if (!IN_LEV_FIELD(jx, jy))
3884 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3885 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3886 printf("DrawPlayerField(): This should never happen!\n");
3891 if (element == EL_EXPLOSION)
3894 action = (player->is_pushing ? ACTION_PUSHING :
3895 player->is_digging ? ACTION_DIGGING :
3896 player->is_collecting ? ACTION_COLLECTING :
3897 player->is_moving ? ACTION_MOVING :
3898 player->is_snapping ? ACTION_SNAPPING :
3899 player->is_dropping ? ACTION_DROPPING :
3900 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3902 if (player->is_waiting)
3903 move_dir = player->dir_waiting;
3905 InitPlayerGfxAnimation(player, action, move_dir);
3907 // --------------------------------------------------------------------------
3908 // draw things in the field the player is leaving, if needed
3909 // --------------------------------------------------------------------------
3911 if (player->is_moving)
3913 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3915 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3917 if (last_element == EL_DYNAMITE_ACTIVE ||
3918 last_element == EL_EM_DYNAMITE_ACTIVE ||
3919 last_element == EL_SP_DISK_RED_ACTIVE)
3920 DrawDynamite(last_jx, last_jy);
3922 DrawLevelFieldThruMask(last_jx, last_jy);
3924 else if (last_element == EL_DYNAMITE_ACTIVE ||
3925 last_element == EL_EM_DYNAMITE_ACTIVE ||
3926 last_element == EL_SP_DISK_RED_ACTIVE)
3927 DrawDynamite(last_jx, last_jy);
3929 /* !!! this is not enough to prevent flickering of players which are
3930 moving next to each others without a free tile between them -- this
3931 can only be solved by drawing all players layer by layer (first the
3932 background, then the foreground etc.) !!! => TODO */
3933 else if (!IS_PLAYER(last_jx, last_jy))
3934 DrawLevelField(last_jx, last_jy);
3937 DrawLevelField(last_jx, last_jy);
3940 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3941 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3944 if (!IN_SCR_FIELD(sx, sy))
3947 // --------------------------------------------------------------------------
3948 // draw things behind the player, if needed
3949 // --------------------------------------------------------------------------
3952 DrawLevelElement(jx, jy, Back[jx][jy]);
3953 else if (IS_ACTIVE_BOMB(element))
3954 DrawLevelElement(jx, jy, EL_EMPTY);
3957 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3959 int old_element = GfxElement[jx][jy];
3960 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3961 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3963 if (GFX_CRUMBLED(old_element))
3964 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3966 DrawGraphic(sx, sy, old_graphic, frame);
3968 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3969 player_is_opaque = TRUE;
3973 GfxElement[jx][jy] = EL_UNDEFINED;
3975 // make sure that pushed elements are drawn with correct frame rate
3976 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3978 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3979 GfxFrame[jx][jy] = player->StepFrame;
3981 DrawLevelField(jx, jy);
3985 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3986 // -----------------------------------------------------------------------
3987 // draw player himself
3988 // -----------------------------------------------------------------------
3990 graphic = getPlayerGraphic(player, move_dir);
3992 // in the case of changed player action or direction, prevent the current
3993 // animation frame from being restarted for identical animations
3994 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3995 player->Frame = last_player_frame;
3997 frame = getGraphicAnimationFrame(graphic, player->Frame);
4001 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4002 sxx = player->GfxPos;
4004 syy = player->GfxPos;
4007 if (player_is_opaque)
4008 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4010 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4012 if (SHIELD_ON(player))
4014 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4015 IMG_SHIELD_NORMAL_ACTIVE);
4016 int frame = getGraphicAnimationFrame(graphic, -1);
4018 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4022 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4025 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4026 sxx = player->GfxPos;
4028 syy = player->GfxPos;
4032 // --------------------------------------------------------------------------
4033 // draw things the player is pushing, if needed
4034 // --------------------------------------------------------------------------
4036 if (player->is_pushing && player->is_moving)
4038 int px = SCREENX(jx), py = SCREENY(jy);
4039 int pxx = (TILEX - ABS(sxx)) * dx;
4040 int pyy = (TILEY - ABS(syy)) * dy;
4041 int gfx_frame = GfxFrame[jx][jy];
4047 if (!IS_MOVING(jx, jy)) // push movement already finished
4049 element = Feld[next_jx][next_jy];
4050 gfx_frame = GfxFrame[next_jx][next_jy];
4053 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4055 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4056 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]);
4077 // do not draw (EM style) pushing animation when pushing is finished
4078 // (two-tile animations usually do not contain start and end frame)
4079 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4080 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4082 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4084 // masked drawing is needed for EMC style (double) movement graphics
4085 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4086 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4090 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4091 // -----------------------------------------------------------------------
4092 // draw player himself
4093 // -----------------------------------------------------------------------
4095 graphic = getPlayerGraphic(player, move_dir);
4097 // in the case of changed player action or direction, prevent the current
4098 // animation frame from being restarted for identical animations
4099 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4100 player->Frame = last_player_frame;
4102 frame = getGraphicAnimationFrame(graphic, player->Frame);
4106 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4107 sxx = player->GfxPos;
4109 syy = player->GfxPos;
4112 if (player_is_opaque)
4113 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4115 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4117 if (SHIELD_ON(player))
4119 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4120 IMG_SHIELD_NORMAL_ACTIVE);
4121 int frame = getGraphicAnimationFrame(graphic, -1);
4123 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4127 // --------------------------------------------------------------------------
4128 // draw things in front of player (active dynamite or dynabombs)
4129 // --------------------------------------------------------------------------
4131 if (IS_ACTIVE_BOMB(element))
4133 graphic = el2img(element);
4134 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4136 if (game.emulation == EMU_SUPAPLEX)
4137 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4139 DrawGraphicThruMask(sx, sy, graphic, frame);
4142 if (player_is_moving && last_element == EL_EXPLOSION)
4144 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4145 GfxElement[last_jx][last_jy] : EL_EMPTY);
4146 int graphic = el_act2img(element, ACTION_EXPLODING);
4147 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4148 int phase = ExplodePhase[last_jx][last_jy] - 1;
4149 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4152 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4155 // --------------------------------------------------------------------------
4156 // draw elements the player is just walking/passing through/under
4157 // --------------------------------------------------------------------------
4159 if (player_is_moving)
4161 // handle the field the player is leaving ...
4162 if (IS_ACCESSIBLE_INSIDE(last_element))
4163 DrawLevelField(last_jx, last_jy);
4164 else if (IS_ACCESSIBLE_UNDER(last_element))
4165 DrawLevelFieldThruMask(last_jx, last_jy);
4168 // do not redraw accessible elements if the player is just pushing them
4169 if (!player_is_moving || !player->is_pushing)
4171 // ... and the field the player is entering
4172 if (IS_ACCESSIBLE_INSIDE(element))
4173 DrawLevelField(jx, jy);
4174 else if (IS_ACCESSIBLE_UNDER(element))
4175 DrawLevelFieldThruMask(jx, jy);
4178 MarkTileDirty(sx, sy);
4181 // ----------------------------------------------------------------------------
4183 void WaitForEventToContinue(void)
4185 boolean still_wait = TRUE;
4187 if (program.headless)
4190 // simulate releasing mouse button over last gadget, if still pressed
4192 HandleGadgets(-1, -1, 0);
4194 button_status = MB_RELEASED;
4202 if (NextValidEvent(&event))
4206 case EVENT_BUTTONRELEASE:
4207 case EVENT_KEYPRESS:
4208 case SDL_CONTROLLERBUTTONDOWN:
4209 case SDL_JOYBUTTONDOWN:
4213 case EVENT_KEYRELEASE:
4214 ClearPlayerAction();
4218 HandleOtherEvents(&event);
4222 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4231 #define MAX_REQUEST_LINES 13
4232 #define MAX_REQUEST_LINE_FONT1_LEN 7
4233 #define MAX_REQUEST_LINE_FONT2_LEN 10
4235 static int RequestHandleEvents(unsigned int req_state)
4237 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4239 int width = request.width;
4240 int height = request.height;
4244 // when showing request dialog after game ended, deactivate game panel
4245 if (game_just_ended)
4246 game.panel.active = FALSE;
4248 game.request_active = TRUE;
4250 setRequestPosition(&sx, &sy, FALSE);
4252 button_status = MB_RELEASED;
4254 request_gadget_id = -1;
4259 if (game_just_ended)
4261 // the MM game engine does not use a special (scrollable) field buffer
4262 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4263 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4265 HandleGameActions();
4267 SetDrawtoField(DRAW_TO_BACKBUFFER);
4269 if (global.use_envelope_request)
4271 // copy current state of request area to middle of playfield area
4272 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4280 while (NextValidEvent(&event))
4284 case EVENT_BUTTONPRESS:
4285 case EVENT_BUTTONRELEASE:
4286 case EVENT_MOTIONNOTIFY:
4290 if (event.type == EVENT_MOTIONNOTIFY)
4295 motion_status = TRUE;
4296 mx = ((MotionEvent *) &event)->x;
4297 my = ((MotionEvent *) &event)->y;
4301 motion_status = FALSE;
4302 mx = ((ButtonEvent *) &event)->x;
4303 my = ((ButtonEvent *) &event)->y;
4304 if (event.type == EVENT_BUTTONPRESS)
4305 button_status = ((ButtonEvent *) &event)->button;
4307 button_status = MB_RELEASED;
4310 // this sets 'request_gadget_id'
4311 HandleGadgets(mx, my, button_status);
4313 switch (request_gadget_id)
4315 case TOOL_CTRL_ID_YES:
4318 case TOOL_CTRL_ID_NO:
4321 case TOOL_CTRL_ID_CONFIRM:
4322 result = TRUE | FALSE;
4325 case TOOL_CTRL_ID_PLAYER_1:
4328 case TOOL_CTRL_ID_PLAYER_2:
4331 case TOOL_CTRL_ID_PLAYER_3:
4334 case TOOL_CTRL_ID_PLAYER_4:
4345 case SDL_WINDOWEVENT:
4346 HandleWindowEvent((WindowEvent *) &event);
4349 case SDL_APP_WILLENTERBACKGROUND:
4350 case SDL_APP_DIDENTERBACKGROUND:
4351 case SDL_APP_WILLENTERFOREGROUND:
4352 case SDL_APP_DIDENTERFOREGROUND:
4353 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4356 case EVENT_KEYPRESS:
4358 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4363 if (req_state & REQ_CONFIRM)
4372 #if defined(KSYM_Rewind)
4373 case KSYM_Rewind: // for Amazon Fire TV remote
4382 #if defined(KSYM_FastForward)
4383 case KSYM_FastForward: // for Amazon Fire TV remote
4389 HandleKeysDebug(key);
4393 if (req_state & REQ_PLAYER)
4395 int old_player_nr = setup.network_player_nr;
4398 result = old_player_nr + 1;
4403 result = old_player_nr + 1;
4434 case EVENT_KEYRELEASE:
4435 ClearPlayerAction();
4438 case SDL_CONTROLLERBUTTONDOWN:
4439 switch (event.cbutton.button)
4441 case SDL_CONTROLLER_BUTTON_A:
4442 case SDL_CONTROLLER_BUTTON_X:
4443 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4444 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4448 case SDL_CONTROLLER_BUTTON_B:
4449 case SDL_CONTROLLER_BUTTON_Y:
4450 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4451 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4452 case SDL_CONTROLLER_BUTTON_BACK:
4457 if (req_state & REQ_PLAYER)
4459 int old_player_nr = setup.network_player_nr;
4462 result = old_player_nr + 1;
4464 switch (event.cbutton.button)
4466 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4467 case SDL_CONTROLLER_BUTTON_Y:
4471 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4472 case SDL_CONTROLLER_BUTTON_B:
4476 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4477 case SDL_CONTROLLER_BUTTON_A:
4481 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4482 case SDL_CONTROLLER_BUTTON_X:
4493 case SDL_CONTROLLERBUTTONUP:
4494 HandleJoystickEvent(&event);
4495 ClearPlayerAction();
4499 HandleOtherEvents(&event);
4504 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4506 int joy = AnyJoystick();
4508 if (joy & JOY_BUTTON_1)
4510 else if (joy & JOY_BUTTON_2)
4513 else if (AnyJoystick())
4515 int joy = AnyJoystick();
4517 if (req_state & REQ_PLAYER)
4521 else if (joy & JOY_RIGHT)
4523 else if (joy & JOY_DOWN)
4525 else if (joy & JOY_LEFT)
4530 if (game_just_ended)
4532 if (global.use_envelope_request)
4534 // copy back current state of pressed buttons inside request area
4535 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4542 game.request_active = FALSE;
4547 static boolean RequestDoor(char *text, unsigned int req_state)
4549 unsigned int old_door_state;
4550 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4551 int font_nr = FONT_TEXT_2;
4556 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4558 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4559 font_nr = FONT_TEXT_1;
4562 if (game_status == GAME_MODE_PLAYING)
4563 BlitScreenToBitmap(backbuffer);
4565 // disable deactivated drawing when quick-loading level tape recording
4566 if (tape.playing && tape.deactivate_display)
4567 TapeDeactivateDisplayOff(TRUE);
4569 SetMouseCursor(CURSOR_DEFAULT);
4571 // pause network game while waiting for request to answer
4572 if (network.enabled &&
4573 game_status == GAME_MODE_PLAYING &&
4574 !game.all_players_gone &&
4575 req_state & REQUEST_WAIT_FOR_INPUT)
4576 SendToServer_PausePlaying();
4578 old_door_state = GetDoorState();
4580 // simulate releasing mouse button over last gadget, if still pressed
4582 HandleGadgets(-1, -1, 0);
4586 // draw released gadget before proceeding
4589 if (old_door_state & DOOR_OPEN_1)
4591 CloseDoor(DOOR_CLOSE_1);
4593 // save old door content
4594 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4595 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4598 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4599 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4601 // clear door drawing field
4602 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4604 // force DOOR font inside door area
4605 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4607 // write text for request
4608 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4610 char text_line[max_request_line_len + 1];
4616 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4618 tc = *(text_ptr + tx);
4619 // if (!tc || tc == ' ')
4620 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4624 if ((tc == '?' || tc == '!') && tl == 0)
4634 strncpy(text_line, text_ptr, tl);
4637 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4638 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4639 text_line, font_nr);
4641 text_ptr += tl + (tc == ' ' ? 1 : 0);
4642 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4647 if (req_state & REQ_ASK)
4649 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4650 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4652 else if (req_state & REQ_CONFIRM)
4654 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4656 else if (req_state & REQ_PLAYER)
4658 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4659 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4660 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4661 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4664 // copy request gadgets to door backbuffer
4665 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4667 OpenDoor(DOOR_OPEN_1);
4669 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4671 if (game_status == GAME_MODE_PLAYING)
4673 SetPanelBackground();
4674 SetDrawBackgroundMask(REDRAW_DOOR_1);
4678 SetDrawBackgroundMask(REDRAW_FIELD);
4684 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4686 // ---------- handle request buttons ----------
4687 result = RequestHandleEvents(req_state);
4691 if (!(req_state & REQ_STAY_OPEN))
4693 CloseDoor(DOOR_CLOSE_1);
4695 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4696 (req_state & REQ_REOPEN))
4697 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4702 if (game_status == GAME_MODE_PLAYING)
4704 SetPanelBackground();
4705 SetDrawBackgroundMask(REDRAW_DOOR_1);
4709 SetDrawBackgroundMask(REDRAW_FIELD);
4712 // continue network game after request
4713 if (network.enabled &&
4714 game_status == GAME_MODE_PLAYING &&
4715 !game.all_players_gone &&
4716 req_state & REQUEST_WAIT_FOR_INPUT)
4717 SendToServer_ContinuePlaying();
4719 // restore deactivated drawing when quick-loading level tape recording
4720 if (tape.playing && tape.deactivate_display)
4721 TapeDeactivateDisplayOn();
4726 static boolean RequestEnvelope(char *text, unsigned int req_state)
4730 if (game_status == GAME_MODE_PLAYING)
4731 BlitScreenToBitmap(backbuffer);
4733 // disable deactivated drawing when quick-loading level tape recording
4734 if (tape.playing && tape.deactivate_display)
4735 TapeDeactivateDisplayOff(TRUE);
4737 SetMouseCursor(CURSOR_DEFAULT);
4739 // pause network game while waiting for request to answer
4740 if (network.enabled &&
4741 game_status == GAME_MODE_PLAYING &&
4742 !game.all_players_gone &&
4743 req_state & REQUEST_WAIT_FOR_INPUT)
4744 SendToServer_PausePlaying();
4746 // simulate releasing mouse button over last gadget, if still pressed
4748 HandleGadgets(-1, -1, 0);
4752 // (replace with setting corresponding request background)
4753 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4754 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4756 // clear door drawing field
4757 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4759 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4761 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4763 if (game_status == GAME_MODE_PLAYING)
4765 SetPanelBackground();
4766 SetDrawBackgroundMask(REDRAW_DOOR_1);
4770 SetDrawBackgroundMask(REDRAW_FIELD);
4776 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4778 // ---------- handle request buttons ----------
4779 result = RequestHandleEvents(req_state);
4783 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4787 if (game_status == GAME_MODE_PLAYING)
4789 SetPanelBackground();
4790 SetDrawBackgroundMask(REDRAW_DOOR_1);
4794 SetDrawBackgroundMask(REDRAW_FIELD);
4797 // continue network game after request
4798 if (network.enabled &&
4799 game_status == GAME_MODE_PLAYING &&
4800 !game.all_players_gone &&
4801 req_state & REQUEST_WAIT_FOR_INPUT)
4802 SendToServer_ContinuePlaying();
4804 // restore deactivated drawing when quick-loading level tape recording
4805 if (tape.playing && tape.deactivate_display)
4806 TapeDeactivateDisplayOn();
4811 boolean Request(char *text, unsigned int req_state)
4813 boolean overlay_active = GetOverlayActive();
4816 SetOverlayActive(FALSE);
4818 if (global.use_envelope_request)
4819 result = RequestEnvelope(text, req_state);
4821 result = RequestDoor(text, req_state);
4823 SetOverlayActive(overlay_active);
4828 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4830 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4831 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4834 if (dpo1->sort_priority != dpo2->sort_priority)
4835 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4837 compare_result = dpo1->nr - dpo2->nr;
4839 return compare_result;
4842 void InitGraphicCompatibilityInfo_Doors(void)
4848 struct DoorInfo *door;
4852 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4853 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4855 { -1, -1, -1, NULL }
4857 struct Rect door_rect_list[] =
4859 { DX, DY, DXSIZE, DYSIZE },
4860 { VX, VY, VXSIZE, VYSIZE }
4864 for (i = 0; doors[i].door_token != -1; i++)
4866 int door_token = doors[i].door_token;
4867 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4868 int part_1 = doors[i].part_1;
4869 int part_8 = doors[i].part_8;
4870 int part_2 = part_1 + 1;
4871 int part_3 = part_1 + 2;
4872 struct DoorInfo *door = doors[i].door;
4873 struct Rect *door_rect = &door_rect_list[door_index];
4874 boolean door_gfx_redefined = FALSE;
4876 // check if any door part graphic definitions have been redefined
4878 for (j = 0; door_part_controls[j].door_token != -1; j++)
4880 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4881 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4883 if (dpc->door_token == door_token && fi->redefined)
4884 door_gfx_redefined = TRUE;
4887 // check for old-style door graphic/animation modifications
4889 if (!door_gfx_redefined)
4891 if (door->anim_mode & ANIM_STATIC_PANEL)
4893 door->panel.step_xoffset = 0;
4894 door->panel.step_yoffset = 0;
4897 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4899 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4900 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4901 int num_door_steps, num_panel_steps;
4903 // remove door part graphics other than the two default wings
4905 for (j = 0; door_part_controls[j].door_token != -1; j++)
4907 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4908 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4910 if (dpc->graphic >= part_3 &&
4911 dpc->graphic <= part_8)
4915 // set graphics and screen positions of the default wings
4917 g_part_1->width = door_rect->width;
4918 g_part_1->height = door_rect->height;
4919 g_part_2->width = door_rect->width;
4920 g_part_2->height = door_rect->height;
4921 g_part_2->src_x = door_rect->width;
4922 g_part_2->src_y = g_part_1->src_y;
4924 door->part_2.x = door->part_1.x;
4925 door->part_2.y = door->part_1.y;
4927 if (door->width != -1)
4929 g_part_1->width = door->width;
4930 g_part_2->width = door->width;
4932 // special treatment for graphics and screen position of right wing
4933 g_part_2->src_x += door_rect->width - door->width;
4934 door->part_2.x += door_rect->width - door->width;
4937 if (door->height != -1)
4939 g_part_1->height = door->height;
4940 g_part_2->height = door->height;
4942 // special treatment for graphics and screen position of bottom wing
4943 g_part_2->src_y += door_rect->height - door->height;
4944 door->part_2.y += door_rect->height - door->height;
4947 // set animation delays for the default wings and panels
4949 door->part_1.step_delay = door->step_delay;
4950 door->part_2.step_delay = door->step_delay;
4951 door->panel.step_delay = door->step_delay;
4953 // set animation draw order for the default wings
4955 door->part_1.sort_priority = 2; // draw left wing over ...
4956 door->part_2.sort_priority = 1; // ... right wing
4958 // set animation draw offset for the default wings
4960 if (door->anim_mode & ANIM_HORIZONTAL)
4962 door->part_1.step_xoffset = door->step_offset;
4963 door->part_1.step_yoffset = 0;
4964 door->part_2.step_xoffset = door->step_offset * -1;
4965 door->part_2.step_yoffset = 0;
4967 num_door_steps = g_part_1->width / door->step_offset;
4969 else // ANIM_VERTICAL
4971 door->part_1.step_xoffset = 0;
4972 door->part_1.step_yoffset = door->step_offset;
4973 door->part_2.step_xoffset = 0;
4974 door->part_2.step_yoffset = door->step_offset * -1;
4976 num_door_steps = g_part_1->height / door->step_offset;
4979 // set animation draw offset for the default panels
4981 if (door->step_offset > 1)
4983 num_panel_steps = 2 * door_rect->height / door->step_offset;
4984 door->panel.start_step = num_panel_steps - num_door_steps;
4985 door->panel.start_step_closing = door->panel.start_step;
4989 num_panel_steps = door_rect->height / door->step_offset;
4990 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4991 door->panel.start_step_closing = door->panel.start_step;
4992 door->panel.step_delay *= 2;
4999 void InitDoors(void)
5003 for (i = 0; door_part_controls[i].door_token != -1; i++)
5005 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5006 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5008 // initialize "start_step_opening" and "start_step_closing", if needed
5009 if (dpc->pos->start_step_opening == 0 &&
5010 dpc->pos->start_step_closing == 0)
5012 // dpc->pos->start_step_opening = dpc->pos->start_step;
5013 dpc->pos->start_step_closing = dpc->pos->start_step;
5016 // fill structure for door part draw order (sorted below)
5018 dpo->sort_priority = dpc->pos->sort_priority;
5021 // sort door part controls according to sort_priority and graphic number
5022 qsort(door_part_order, MAX_DOOR_PARTS,
5023 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5026 unsigned int OpenDoor(unsigned int door_state)
5028 if (door_state & DOOR_COPY_BACK)
5030 if (door_state & DOOR_OPEN_1)
5031 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5032 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5034 if (door_state & DOOR_OPEN_2)
5035 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5036 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5038 door_state &= ~DOOR_COPY_BACK;
5041 return MoveDoor(door_state);
5044 unsigned int CloseDoor(unsigned int door_state)
5046 unsigned int old_door_state = GetDoorState();
5048 if (!(door_state & DOOR_NO_COPY_BACK))
5050 if (old_door_state & DOOR_OPEN_1)
5051 BlitBitmap(backbuffer, bitmap_db_door_1,
5052 DX, DY, DXSIZE, DYSIZE, 0, 0);
5054 if (old_door_state & DOOR_OPEN_2)
5055 BlitBitmap(backbuffer, bitmap_db_door_2,
5056 VX, VY, VXSIZE, VYSIZE, 0, 0);
5058 door_state &= ~DOOR_NO_COPY_BACK;
5061 return MoveDoor(door_state);
5064 unsigned int GetDoorState(void)
5066 return MoveDoor(DOOR_GET_STATE);
5069 unsigned int SetDoorState(unsigned int door_state)
5071 return MoveDoor(door_state | DOOR_SET_STATE);
5074 static int euclid(int a, int b)
5076 return (b ? euclid(b, a % b) : a);
5079 unsigned int MoveDoor(unsigned int door_state)
5081 struct Rect door_rect_list[] =
5083 { DX, DY, DXSIZE, DYSIZE },
5084 { VX, VY, VXSIZE, VYSIZE }
5086 static int door1 = DOOR_CLOSE_1;
5087 static int door2 = DOOR_CLOSE_2;
5088 unsigned int door_delay = 0;
5089 unsigned int door_delay_value;
5092 if (door_state == DOOR_GET_STATE)
5093 return (door1 | door2);
5095 if (door_state & DOOR_SET_STATE)
5097 if (door_state & DOOR_ACTION_1)
5098 door1 = door_state & DOOR_ACTION_1;
5099 if (door_state & DOOR_ACTION_2)
5100 door2 = door_state & DOOR_ACTION_2;
5102 return (door1 | door2);
5105 if (!(door_state & DOOR_FORCE_REDRAW))
5107 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5108 door_state &= ~DOOR_OPEN_1;
5109 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5110 door_state &= ~DOOR_CLOSE_1;
5111 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5112 door_state &= ~DOOR_OPEN_2;
5113 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5114 door_state &= ~DOOR_CLOSE_2;
5117 if (global.autoplay_leveldir)
5119 door_state |= DOOR_NO_DELAY;
5120 door_state &= ~DOOR_CLOSE_ALL;
5123 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5124 door_state |= DOOR_NO_DELAY;
5126 if (door_state & DOOR_ACTION)
5128 boolean door_panel_drawn[NUM_DOORS];
5129 boolean panel_has_doors[NUM_DOORS];
5130 boolean door_part_skip[MAX_DOOR_PARTS];
5131 boolean door_part_done[MAX_DOOR_PARTS];
5132 boolean door_part_done_all;
5133 int num_steps[MAX_DOOR_PARTS];
5134 int max_move_delay = 0; // delay for complete animations of all doors
5135 int max_step_delay = 0; // delay (ms) between two animation frames
5136 int num_move_steps = 0; // number of animation steps for all doors
5137 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5138 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5139 int current_move_delay = 0;
5143 for (i = 0; i < NUM_DOORS; i++)
5144 panel_has_doors[i] = FALSE;
5146 for (i = 0; i < MAX_DOOR_PARTS; i++)
5148 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5149 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5150 int door_token = dpc->door_token;
5152 door_part_done[i] = FALSE;
5153 door_part_skip[i] = (!(door_state & door_token) ||
5157 for (i = 0; i < MAX_DOOR_PARTS; i++)
5159 int nr = door_part_order[i].nr;
5160 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5161 struct DoorPartPosInfo *pos = dpc->pos;
5162 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5163 int door_token = dpc->door_token;
5164 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5165 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5166 int step_xoffset = ABS(pos->step_xoffset);
5167 int step_yoffset = ABS(pos->step_yoffset);
5168 int step_delay = pos->step_delay;
5169 int current_door_state = door_state & door_token;
5170 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5171 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5172 boolean part_opening = (is_panel ? door_closing : door_opening);
5173 int start_step = (part_opening ? pos->start_step_opening :
5174 pos->start_step_closing);
5175 float move_xsize = (step_xoffset ? g->width : 0);
5176 float move_ysize = (step_yoffset ? g->height : 0);
5177 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5178 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5179 int move_steps = (move_xsteps && move_ysteps ?
5180 MIN(move_xsteps, move_ysteps) :
5181 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5182 int move_delay = move_steps * step_delay;
5184 if (door_part_skip[nr])
5187 max_move_delay = MAX(max_move_delay, move_delay);
5188 max_step_delay = (max_step_delay == 0 ? step_delay :
5189 euclid(max_step_delay, step_delay));
5190 num_steps[nr] = move_steps;
5194 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5196 panel_has_doors[door_index] = TRUE;
5200 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5202 num_move_steps = max_move_delay / max_step_delay;
5203 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5205 door_delay_value = max_step_delay;
5207 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5209 start = num_move_steps - 1;
5213 // opening door sound has priority over simultaneously closing door
5214 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5216 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5218 if (door_state & DOOR_OPEN_1)
5219 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5220 if (door_state & DOOR_OPEN_2)
5221 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5223 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5225 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5227 if (door_state & DOOR_CLOSE_1)
5228 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5229 if (door_state & DOOR_CLOSE_2)
5230 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5234 for (k = start; k < num_move_steps; k++)
5236 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5238 door_part_done_all = TRUE;
5240 for (i = 0; i < NUM_DOORS; i++)
5241 door_panel_drawn[i] = FALSE;
5243 for (i = 0; i < MAX_DOOR_PARTS; i++)
5245 int nr = door_part_order[i].nr;
5246 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5247 struct DoorPartPosInfo *pos = dpc->pos;
5248 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5249 int door_token = dpc->door_token;
5250 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5251 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5252 boolean is_panel_and_door_has_closed = FALSE;
5253 struct Rect *door_rect = &door_rect_list[door_index];
5254 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5256 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5257 int current_door_state = door_state & door_token;
5258 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5259 boolean door_closing = !door_opening;
5260 boolean part_opening = (is_panel ? door_closing : door_opening);
5261 boolean part_closing = !part_opening;
5262 int start_step = (part_opening ? pos->start_step_opening :
5263 pos->start_step_closing);
5264 int step_delay = pos->step_delay;
5265 int step_factor = step_delay / max_step_delay;
5266 int k1 = (step_factor ? k / step_factor + 1 : k);
5267 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5268 int kk = MAX(0, k2);
5271 int src_x, src_y, src_xx, src_yy;
5272 int dst_x, dst_y, dst_xx, dst_yy;
5275 if (door_part_skip[nr])
5278 if (!(door_state & door_token))
5286 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5287 int kk_door = MAX(0, k2_door);
5288 int sync_frame = kk_door * door_delay_value;
5289 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5291 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5292 &g_src_x, &g_src_y);
5297 if (!door_panel_drawn[door_index])
5299 ClearRectangle(drawto, door_rect->x, door_rect->y,
5300 door_rect->width, door_rect->height);
5302 door_panel_drawn[door_index] = TRUE;
5305 // draw opening or closing door parts
5307 if (pos->step_xoffset < 0) // door part on right side
5310 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5313 if (dst_xx + width > door_rect->width)
5314 width = door_rect->width - dst_xx;
5316 else // door part on left side
5319 dst_xx = pos->x - kk * pos->step_xoffset;
5323 src_xx = ABS(dst_xx);
5327 width = g->width - src_xx;
5329 if (width > door_rect->width)
5330 width = door_rect->width;
5332 // printf("::: k == %d [%d] \n", k, start_step);
5335 if (pos->step_yoffset < 0) // door part on bottom side
5338 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5341 if (dst_yy + height > door_rect->height)
5342 height = door_rect->height - dst_yy;
5344 else // door part on top side
5347 dst_yy = pos->y - kk * pos->step_yoffset;
5351 src_yy = ABS(dst_yy);
5355 height = g->height - src_yy;
5358 src_x = g_src_x + src_xx;
5359 src_y = g_src_y + src_yy;
5361 dst_x = door_rect->x + dst_xx;
5362 dst_y = door_rect->y + dst_yy;
5364 is_panel_and_door_has_closed =
5367 panel_has_doors[door_index] &&
5368 k >= num_move_steps_doors_only - 1);
5370 if (width >= 0 && width <= g->width &&
5371 height >= 0 && height <= g->height &&
5372 !is_panel_and_door_has_closed)
5374 if (is_panel || !pos->draw_masked)
5375 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5378 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5382 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5384 if ((part_opening && (width < 0 || height < 0)) ||
5385 (part_closing && (width >= g->width && height >= g->height)))
5386 door_part_done[nr] = TRUE;
5388 // continue door part animations, but not panel after door has closed
5389 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5390 door_part_done_all = FALSE;
5393 if (!(door_state & DOOR_NO_DELAY))
5397 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5399 current_move_delay += max_step_delay;
5401 // prevent OS (Windows) from complaining about program not responding
5405 if (door_part_done_all)
5409 if (!(door_state & DOOR_NO_DELAY))
5411 // wait for specified door action post delay
5412 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5413 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5414 else if (door_state & DOOR_ACTION_1)
5415 door_delay_value = door_1.post_delay;
5416 else if (door_state & DOOR_ACTION_2)
5417 door_delay_value = door_2.post_delay;
5419 while (!DelayReached(&door_delay, door_delay_value))
5424 if (door_state & DOOR_ACTION_1)
5425 door1 = door_state & DOOR_ACTION_1;
5426 if (door_state & DOOR_ACTION_2)
5427 door2 = door_state & DOOR_ACTION_2;
5429 // draw masked border over door area
5430 DrawMaskedBorder(REDRAW_DOOR_1);
5431 DrawMaskedBorder(REDRAW_DOOR_2);
5433 ClearAutoRepeatKeyEvents();
5435 return (door1 | door2);
5438 static boolean useSpecialEditorDoor(void)
5440 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5441 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5443 // do not draw special editor door if editor border defined or redefined
5444 if (graphic_info[graphic].bitmap != NULL || redefined)
5447 // do not draw special editor door if global border defined to be empty
5448 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5451 // do not draw special editor door if viewport definitions do not match
5455 EY + EYSIZE != VY + VYSIZE)
5461 void DrawSpecialEditorDoor(void)
5463 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5464 int top_border_width = gfx1->width;
5465 int top_border_height = gfx1->height;
5466 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5467 int ex = EX - outer_border;
5468 int ey = EY - outer_border;
5469 int vy = VY - outer_border;
5470 int exsize = EXSIZE + 2 * outer_border;
5472 if (!useSpecialEditorDoor())
5475 // draw bigger level editor toolbox window
5476 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5477 top_border_width, top_border_height, ex, ey - top_border_height);
5478 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5479 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5481 redraw_mask |= REDRAW_ALL;
5484 void UndrawSpecialEditorDoor(void)
5486 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5487 int top_border_width = gfx1->width;
5488 int top_border_height = gfx1->height;
5489 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5490 int ex = EX - outer_border;
5491 int ey = EY - outer_border;
5492 int ey_top = ey - top_border_height;
5493 int exsize = EXSIZE + 2 * outer_border;
5494 int eysize = EYSIZE + 2 * outer_border;
5496 if (!useSpecialEditorDoor())
5499 // draw normal tape recorder window
5500 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5502 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5503 ex, ey_top, top_border_width, top_border_height,
5505 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5506 ex, ey, exsize, eysize, ex, ey);
5510 // if screen background is set to "[NONE]", clear editor toolbox window
5511 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5512 ClearRectangle(drawto, ex, ey, exsize, eysize);
5515 redraw_mask |= REDRAW_ALL;
5519 // ---------- new tool button stuff -------------------------------------------
5524 struct TextPosInfo *pos;
5527 } toolbutton_info[NUM_TOOL_BUTTONS] =
5530 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5531 TOOL_CTRL_ID_YES, "yes"
5534 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5535 TOOL_CTRL_ID_NO, "no"
5538 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5539 TOOL_CTRL_ID_CONFIRM, "confirm"
5542 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5543 TOOL_CTRL_ID_PLAYER_1, "player 1"
5546 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5547 TOOL_CTRL_ID_PLAYER_2, "player 2"
5550 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5551 TOOL_CTRL_ID_PLAYER_3, "player 3"
5554 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5555 TOOL_CTRL_ID_PLAYER_4, "player 4"
5559 void CreateToolButtons(void)
5563 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5565 int graphic = toolbutton_info[i].graphic;
5566 struct GraphicInfo *gfx = &graphic_info[graphic];
5567 struct TextPosInfo *pos = toolbutton_info[i].pos;
5568 struct GadgetInfo *gi;
5569 Bitmap *deco_bitmap = None;
5570 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5571 unsigned int event_mask = GD_EVENT_RELEASED;
5574 int gd_x = gfx->src_x;
5575 int gd_y = gfx->src_y;
5576 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5577 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5582 if (global.use_envelope_request)
5584 setRequestPosition(&dx, &dy, TRUE);
5586 // check if request buttons are outside of envelope and fix, if needed
5587 if (x < 0 || x + gfx->width > request.width ||
5588 y < 0 || y + gfx->height > request.height)
5590 if (id == TOOL_CTRL_ID_YES)
5593 y = request.height - 2 * request.border_size - gfx->height;
5595 else if (id == TOOL_CTRL_ID_NO)
5597 x = request.width - 2 * request.border_size - gfx->width;
5598 y = request.height - 2 * request.border_size - gfx->height;
5600 else if (id == TOOL_CTRL_ID_CONFIRM)
5602 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5603 y = request.height - 2 * request.border_size - gfx->height;
5605 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5607 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5609 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5610 y = request.height - 2 * request.border_size - gfx->height * 2;
5612 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5613 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5618 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5620 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5622 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5623 pos->size, &deco_bitmap, &deco_x, &deco_y);
5624 deco_xpos = (gfx->width - pos->size) / 2;
5625 deco_ypos = (gfx->height - pos->size) / 2;
5628 gi = CreateGadget(GDI_CUSTOM_ID, id,
5629 GDI_IMAGE_ID, graphic,
5630 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5633 GDI_WIDTH, gfx->width,
5634 GDI_HEIGHT, gfx->height,
5635 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5636 GDI_STATE, GD_BUTTON_UNPRESSED,
5637 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5638 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5639 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5640 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5641 GDI_DECORATION_SIZE, pos->size, pos->size,
5642 GDI_DECORATION_SHIFTING, 1, 1,
5643 GDI_DIRECT_DRAW, FALSE,
5644 GDI_EVENT_MASK, event_mask,
5645 GDI_CALLBACK_ACTION, HandleToolButtons,
5649 Error(ERR_EXIT, "cannot create gadget");
5651 tool_gadget[id] = gi;
5655 void FreeToolButtons(void)
5659 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5660 FreeGadget(tool_gadget[i]);
5663 static void UnmapToolButtons(void)
5667 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5668 UnmapGadget(tool_gadget[i]);
5671 static void HandleToolButtons(struct GadgetInfo *gi)
5673 request_gadget_id = gi->custom_id;
5676 static struct Mapping_EM_to_RND_object
5679 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5680 boolean is_backside; // backside of moving element
5686 em_object_mapping_list[] =
5689 Xblank, TRUE, FALSE,
5693 Yacid_splash_eB, FALSE, FALSE,
5694 EL_ACID_SPLASH_RIGHT, -1, -1
5697 Yacid_splash_wB, FALSE, FALSE,
5698 EL_ACID_SPLASH_LEFT, -1, -1
5701 #ifdef EM_ENGINE_BAD_ROLL
5703 Xstone_force_e, FALSE, FALSE,
5704 EL_ROCK, -1, MV_BIT_RIGHT
5707 Xstone_force_w, FALSE, FALSE,
5708 EL_ROCK, -1, MV_BIT_LEFT
5711 Xnut_force_e, FALSE, FALSE,
5712 EL_NUT, -1, MV_BIT_RIGHT
5715 Xnut_force_w, FALSE, FALSE,
5716 EL_NUT, -1, MV_BIT_LEFT
5719 Xspring_force_e, FALSE, FALSE,
5720 EL_SPRING, -1, MV_BIT_RIGHT
5723 Xspring_force_w, FALSE, FALSE,
5724 EL_SPRING, -1, MV_BIT_LEFT
5727 Xemerald_force_e, FALSE, FALSE,
5728 EL_EMERALD, -1, MV_BIT_RIGHT
5731 Xemerald_force_w, FALSE, FALSE,
5732 EL_EMERALD, -1, MV_BIT_LEFT
5735 Xdiamond_force_e, FALSE, FALSE,
5736 EL_DIAMOND, -1, MV_BIT_RIGHT
5739 Xdiamond_force_w, FALSE, FALSE,
5740 EL_DIAMOND, -1, MV_BIT_LEFT
5743 Xbomb_force_e, FALSE, FALSE,
5744 EL_BOMB, -1, MV_BIT_RIGHT
5747 Xbomb_force_w, FALSE, FALSE,
5748 EL_BOMB, -1, MV_BIT_LEFT
5750 #endif // EM_ENGINE_BAD_ROLL
5753 Xstone, TRUE, FALSE,
5757 Xstone_pause, FALSE, FALSE,
5761 Xstone_fall, FALSE, FALSE,
5765 Ystone_s, FALSE, FALSE,
5766 EL_ROCK, ACTION_FALLING, -1
5769 Ystone_sB, FALSE, TRUE,
5770 EL_ROCK, ACTION_FALLING, -1
5773 Ystone_e, FALSE, FALSE,
5774 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5777 Ystone_eB, FALSE, TRUE,
5778 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5781 Ystone_w, FALSE, FALSE,
5782 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5785 Ystone_wB, FALSE, TRUE,
5786 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5793 Xnut_pause, FALSE, FALSE,
5797 Xnut_fall, FALSE, FALSE,
5801 Ynut_s, FALSE, FALSE,
5802 EL_NUT, ACTION_FALLING, -1
5805 Ynut_sB, FALSE, TRUE,
5806 EL_NUT, ACTION_FALLING, -1
5809 Ynut_e, FALSE, FALSE,
5810 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5813 Ynut_eB, FALSE, TRUE,
5814 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5817 Ynut_w, FALSE, FALSE,
5818 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5821 Ynut_wB, FALSE, TRUE,
5822 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5825 Xbug_n, TRUE, FALSE,
5829 Xbug_e, TRUE, FALSE,
5830 EL_BUG_RIGHT, -1, -1
5833 Xbug_s, TRUE, FALSE,
5837 Xbug_w, TRUE, FALSE,
5841 Xbug_gon, FALSE, FALSE,
5845 Xbug_goe, FALSE, FALSE,
5846 EL_BUG_RIGHT, -1, -1
5849 Xbug_gos, FALSE, FALSE,
5853 Xbug_gow, FALSE, FALSE,
5857 Ybug_n, FALSE, FALSE,
5858 EL_BUG, ACTION_MOVING, MV_BIT_UP
5861 Ybug_nB, FALSE, TRUE,
5862 EL_BUG, ACTION_MOVING, MV_BIT_UP
5865 Ybug_e, FALSE, FALSE,
5866 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5869 Ybug_eB, FALSE, TRUE,
5870 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5873 Ybug_s, FALSE, FALSE,
5874 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5877 Ybug_sB, FALSE, TRUE,
5878 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5881 Ybug_w, FALSE, FALSE,
5882 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5885 Ybug_wB, FALSE, TRUE,
5886 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5889 Ybug_w_n, FALSE, FALSE,
5890 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5893 Ybug_n_e, FALSE, FALSE,
5894 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5897 Ybug_e_s, FALSE, FALSE,
5898 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5901 Ybug_s_w, FALSE, FALSE,
5902 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5905 Ybug_e_n, FALSE, FALSE,
5906 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5909 Ybug_s_e, FALSE, FALSE,
5910 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5913 Ybug_w_s, FALSE, FALSE,
5914 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5917 Ybug_n_w, FALSE, FALSE,
5918 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5921 Ybug_stone, FALSE, FALSE,
5922 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5925 Ybug_spring, FALSE, FALSE,
5926 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5929 Xtank_n, TRUE, FALSE,
5930 EL_SPACESHIP_UP, -1, -1
5933 Xtank_e, TRUE, FALSE,
5934 EL_SPACESHIP_RIGHT, -1, -1
5937 Xtank_s, TRUE, FALSE,
5938 EL_SPACESHIP_DOWN, -1, -1
5941 Xtank_w, TRUE, FALSE,
5942 EL_SPACESHIP_LEFT, -1, -1
5945 Xtank_gon, FALSE, FALSE,
5946 EL_SPACESHIP_UP, -1, -1
5949 Xtank_goe, FALSE, FALSE,
5950 EL_SPACESHIP_RIGHT, -1, -1
5953 Xtank_gos, FALSE, FALSE,
5954 EL_SPACESHIP_DOWN, -1, -1
5957 Xtank_gow, FALSE, FALSE,
5958 EL_SPACESHIP_LEFT, -1, -1
5961 Ytank_n, FALSE, FALSE,
5962 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5965 Ytank_nB, FALSE, TRUE,
5966 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5969 Ytank_e, FALSE, FALSE,
5970 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5973 Ytank_eB, FALSE, TRUE,
5974 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5977 Ytank_s, FALSE, FALSE,
5978 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5981 Ytank_sB, FALSE, TRUE,
5982 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5985 Ytank_w, FALSE, FALSE,
5986 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5989 Ytank_wB, FALSE, TRUE,
5990 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5993 Ytank_w_n, FALSE, FALSE,
5994 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5997 Ytank_n_e, FALSE, FALSE,
5998 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6001 Ytank_e_s, FALSE, FALSE,
6002 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6005 Ytank_s_w, FALSE, FALSE,
6006 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6009 Ytank_e_n, FALSE, FALSE,
6010 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6013 Ytank_s_e, FALSE, FALSE,
6014 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6017 Ytank_w_s, FALSE, FALSE,
6018 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6021 Ytank_n_w, FALSE, FALSE,
6022 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6025 Ytank_stone, FALSE, FALSE,
6026 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6029 Ytank_spring, FALSE, FALSE,
6030 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6033 Xandroid, TRUE, FALSE,
6034 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6037 Xandroid_1_n, FALSE, FALSE,
6038 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6041 Xandroid_2_n, FALSE, FALSE,
6042 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6045 Xandroid_1_e, FALSE, FALSE,
6046 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6049 Xandroid_2_e, FALSE, FALSE,
6050 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6053 Xandroid_1_w, FALSE, FALSE,
6054 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6057 Xandroid_2_w, FALSE, FALSE,
6058 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6061 Xandroid_1_s, FALSE, FALSE,
6062 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6065 Xandroid_2_s, FALSE, FALSE,
6066 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6069 Yandroid_n, FALSE, FALSE,
6070 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6073 Yandroid_nB, FALSE, TRUE,
6074 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6077 Yandroid_ne, FALSE, FALSE,
6078 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6081 Yandroid_neB, FALSE, TRUE,
6082 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6085 Yandroid_e, FALSE, FALSE,
6086 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6089 Yandroid_eB, FALSE, TRUE,
6090 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6093 Yandroid_se, FALSE, FALSE,
6094 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6097 Yandroid_seB, FALSE, TRUE,
6098 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6101 Yandroid_s, FALSE, FALSE,
6102 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6105 Yandroid_sB, FALSE, TRUE,
6106 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6109 Yandroid_sw, FALSE, FALSE,
6110 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6113 Yandroid_swB, FALSE, TRUE,
6114 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6117 Yandroid_w, FALSE, FALSE,
6118 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6121 Yandroid_wB, FALSE, TRUE,
6122 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6125 Yandroid_nw, FALSE, FALSE,
6126 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6129 Yandroid_nwB, FALSE, TRUE,
6130 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6133 Xspring, TRUE, FALSE,
6137 Xspring_pause, FALSE, FALSE,
6141 Xspring_e, FALSE, FALSE,
6145 Xspring_w, FALSE, FALSE,
6149 Xspring_fall, FALSE, FALSE,
6153 Yspring_s, FALSE, FALSE,
6154 EL_SPRING, ACTION_FALLING, -1
6157 Yspring_sB, FALSE, TRUE,
6158 EL_SPRING, ACTION_FALLING, -1
6161 Yspring_e, FALSE, FALSE,
6162 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6165 Yspring_eB, FALSE, TRUE,
6166 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6169 Yspring_w, FALSE, FALSE,
6170 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6173 Yspring_wB, FALSE, TRUE,
6174 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6177 Yspring_kill_e, FALSE, FALSE,
6178 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6181 Yspring_kill_eB, FALSE, TRUE,
6182 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6185 Yspring_kill_w, FALSE, FALSE,
6186 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6189 Yspring_kill_wB, FALSE, TRUE,
6190 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6193 Xeater_n, TRUE, FALSE,
6194 EL_YAMYAM_UP, -1, -1
6197 Xeater_e, TRUE, FALSE,
6198 EL_YAMYAM_RIGHT, -1, -1
6201 Xeater_w, TRUE, FALSE,
6202 EL_YAMYAM_LEFT, -1, -1
6205 Xeater_s, TRUE, FALSE,
6206 EL_YAMYAM_DOWN, -1, -1
6209 Yeater_n, FALSE, FALSE,
6210 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6213 Yeater_nB, FALSE, TRUE,
6214 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6217 Yeater_e, FALSE, FALSE,
6218 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6221 Yeater_eB, FALSE, TRUE,
6222 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6225 Yeater_s, FALSE, FALSE,
6226 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6229 Yeater_sB, FALSE, TRUE,
6230 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6233 Yeater_w, FALSE, FALSE,
6234 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6237 Yeater_wB, FALSE, TRUE,
6238 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6241 Yeater_stone, FALSE, FALSE,
6242 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6245 Yeater_spring, FALSE, FALSE,
6246 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6249 Xalien, TRUE, FALSE,
6253 Xalien_pause, FALSE, FALSE,
6257 Yalien_n, FALSE, FALSE,
6258 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6261 Yalien_nB, FALSE, TRUE,
6262 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6265 Yalien_e, FALSE, FALSE,
6266 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6269 Yalien_eB, FALSE, TRUE,
6270 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6273 Yalien_s, FALSE, FALSE,
6274 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6277 Yalien_sB, FALSE, TRUE,
6278 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6281 Yalien_w, FALSE, FALSE,
6282 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6285 Yalien_wB, FALSE, TRUE,
6286 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6289 Yalien_stone, FALSE, FALSE,
6290 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6293 Yalien_spring, FALSE, FALSE,
6294 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6297 Xemerald, TRUE, FALSE,
6301 Xemerald_pause, FALSE, FALSE,
6305 Xemerald_fall, FALSE, FALSE,
6309 Xemerald_shine, FALSE, FALSE,
6310 EL_EMERALD, ACTION_TWINKLING, -1
6313 Yemerald_s, FALSE, FALSE,
6314 EL_EMERALD, ACTION_FALLING, -1
6317 Yemerald_sB, FALSE, TRUE,
6318 EL_EMERALD, ACTION_FALLING, -1
6321 Yemerald_e, FALSE, FALSE,
6322 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6325 Yemerald_eB, FALSE, TRUE,
6326 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6329 Yemerald_w, FALSE, FALSE,
6330 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6333 Yemerald_wB, FALSE, TRUE,
6334 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6337 Yemerald_eat, FALSE, FALSE,
6338 EL_EMERALD, ACTION_COLLECTING, -1
6341 Yemerald_stone, FALSE, FALSE,
6342 EL_NUT, ACTION_BREAKING, -1
6345 Xdiamond, TRUE, FALSE,
6349 Xdiamond_pause, FALSE, FALSE,
6353 Xdiamond_fall, FALSE, FALSE,
6357 Xdiamond_shine, FALSE, FALSE,
6358 EL_DIAMOND, ACTION_TWINKLING, -1
6361 Ydiamond_s, FALSE, FALSE,
6362 EL_DIAMOND, ACTION_FALLING, -1
6365 Ydiamond_sB, FALSE, TRUE,
6366 EL_DIAMOND, ACTION_FALLING, -1
6369 Ydiamond_e, FALSE, FALSE,
6370 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6373 Ydiamond_eB, FALSE, TRUE,
6374 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6377 Ydiamond_w, FALSE, FALSE,
6378 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6381 Ydiamond_wB, FALSE, TRUE,
6382 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6385 Ydiamond_eat, FALSE, FALSE,
6386 EL_DIAMOND, ACTION_COLLECTING, -1
6389 Ydiamond_stone, FALSE, FALSE,
6390 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6393 Xdrip_fall, TRUE, FALSE,
6394 EL_AMOEBA_DROP, -1, -1
6397 Xdrip_stretch, FALSE, FALSE,
6398 EL_AMOEBA_DROP, ACTION_FALLING, -1
6401 Xdrip_stretchB, FALSE, TRUE,
6402 EL_AMOEBA_DROP, ACTION_FALLING, -1
6405 Xdrip_eat, FALSE, FALSE,
6406 EL_AMOEBA_DROP, ACTION_GROWING, -1
6409 Ydrip_s1, FALSE, FALSE,
6410 EL_AMOEBA_DROP, ACTION_FALLING, -1
6413 Ydrip_s1B, FALSE, TRUE,
6414 EL_AMOEBA_DROP, ACTION_FALLING, -1
6417 Ydrip_s2, FALSE, FALSE,
6418 EL_AMOEBA_DROP, ACTION_FALLING, -1
6421 Ydrip_s2B, FALSE, TRUE,
6422 EL_AMOEBA_DROP, ACTION_FALLING, -1
6429 Xbomb_pause, FALSE, FALSE,
6433 Xbomb_fall, FALSE, FALSE,
6437 Ybomb_s, FALSE, FALSE,
6438 EL_BOMB, ACTION_FALLING, -1
6441 Ybomb_sB, FALSE, TRUE,
6442 EL_BOMB, ACTION_FALLING, -1
6445 Ybomb_e, FALSE, FALSE,
6446 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6449 Ybomb_eB, FALSE, TRUE,
6450 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6453 Ybomb_w, FALSE, FALSE,
6454 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6457 Ybomb_wB, FALSE, TRUE,
6458 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6461 Ybomb_eat, FALSE, FALSE,
6462 EL_BOMB, ACTION_ACTIVATING, -1
6465 Xballoon, TRUE, FALSE,
6469 Yballoon_n, FALSE, FALSE,
6470 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6473 Yballoon_nB, FALSE, TRUE,
6474 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6477 Yballoon_e, FALSE, FALSE,
6478 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6481 Yballoon_eB, FALSE, TRUE,
6482 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6485 Yballoon_s, FALSE, FALSE,
6486 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6489 Yballoon_sB, FALSE, TRUE,
6490 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6493 Yballoon_w, FALSE, FALSE,
6494 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6497 Yballoon_wB, FALSE, TRUE,
6498 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6501 Xgrass, TRUE, FALSE,
6502 EL_EMC_GRASS, -1, -1
6505 Ygrass_nB, FALSE, FALSE,
6506 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6509 Ygrass_eB, FALSE, FALSE,
6510 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6513 Ygrass_sB, FALSE, FALSE,
6514 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6517 Ygrass_wB, FALSE, FALSE,
6518 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6525 Ydirt_nB, FALSE, FALSE,
6526 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6529 Ydirt_eB, FALSE, FALSE,
6530 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6533 Ydirt_sB, FALSE, FALSE,
6534 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6537 Ydirt_wB, FALSE, FALSE,
6538 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6541 Xacid_ne, TRUE, FALSE,
6542 EL_ACID_POOL_TOPRIGHT, -1, -1
6545 Xacid_se, TRUE, FALSE,
6546 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6549 Xacid_s, TRUE, FALSE,
6550 EL_ACID_POOL_BOTTOM, -1, -1
6553 Xacid_sw, TRUE, FALSE,
6554 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6557 Xacid_nw, TRUE, FALSE,
6558 EL_ACID_POOL_TOPLEFT, -1, -1
6561 Xacid_1, TRUE, FALSE,
6565 Xacid_2, FALSE, FALSE,
6569 Xacid_3, FALSE, FALSE,
6573 Xacid_4, FALSE, FALSE,
6577 Xacid_5, FALSE, FALSE,
6581 Xacid_6, FALSE, FALSE,
6585 Xacid_7, FALSE, FALSE,
6589 Xacid_8, FALSE, FALSE,
6593 Xball_1, TRUE, FALSE,
6594 EL_EMC_MAGIC_BALL, -1, -1
6597 Xball_1B, FALSE, FALSE,
6598 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6601 Xball_2, FALSE, FALSE,
6602 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6605 Xball_2B, FALSE, FALSE,
6606 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6609 Yball_eat, FALSE, FALSE,
6610 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6613 Ykey_1_eat, FALSE, FALSE,
6614 EL_EM_KEY_1, ACTION_COLLECTING, -1
6617 Ykey_2_eat, FALSE, FALSE,
6618 EL_EM_KEY_2, ACTION_COLLECTING, -1
6621 Ykey_3_eat, FALSE, FALSE,
6622 EL_EM_KEY_3, ACTION_COLLECTING, -1
6625 Ykey_4_eat, FALSE, FALSE,
6626 EL_EM_KEY_4, ACTION_COLLECTING, -1
6629 Ykey_5_eat, FALSE, FALSE,
6630 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6633 Ykey_6_eat, FALSE, FALSE,
6634 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6637 Ykey_7_eat, FALSE, FALSE,
6638 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6641 Ykey_8_eat, FALSE, FALSE,
6642 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6645 Ylenses_eat, FALSE, FALSE,
6646 EL_EMC_LENSES, ACTION_COLLECTING, -1
6649 Ymagnify_eat, FALSE, FALSE,
6650 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6653 Ygrass_eat, FALSE, FALSE,
6654 EL_EMC_GRASS, ACTION_SNAPPING, -1
6657 Ydirt_eat, FALSE, FALSE,
6658 EL_SAND, ACTION_SNAPPING, -1
6661 Xgrow_ns, TRUE, FALSE,
6662 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6665 Ygrow_ns_eat, FALSE, FALSE,
6666 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6669 Xgrow_ew, TRUE, FALSE,
6670 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6673 Ygrow_ew_eat, FALSE, FALSE,
6674 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6677 Xwonderwall, TRUE, FALSE,
6678 EL_MAGIC_WALL, -1, -1
6681 XwonderwallB, FALSE, FALSE,
6682 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6685 Xamoeba_1, TRUE, FALSE,
6686 EL_AMOEBA_DRY, ACTION_OTHER, -1
6689 Xamoeba_2, FALSE, FALSE,
6690 EL_AMOEBA_DRY, ACTION_OTHER, -1
6693 Xamoeba_3, FALSE, FALSE,
6694 EL_AMOEBA_DRY, ACTION_OTHER, -1
6697 Xamoeba_4, FALSE, FALSE,
6698 EL_AMOEBA_DRY, ACTION_OTHER, -1
6701 Xamoeba_5, TRUE, FALSE,
6702 EL_AMOEBA_WET, ACTION_OTHER, -1
6705 Xamoeba_6, FALSE, FALSE,
6706 EL_AMOEBA_WET, ACTION_OTHER, -1
6709 Xamoeba_7, FALSE, FALSE,
6710 EL_AMOEBA_WET, ACTION_OTHER, -1
6713 Xamoeba_8, FALSE, FALSE,
6714 EL_AMOEBA_WET, ACTION_OTHER, -1
6717 Xdoor_1, TRUE, FALSE,
6718 EL_EM_GATE_1, -1, -1
6721 Xdoor_2, TRUE, FALSE,
6722 EL_EM_GATE_2, -1, -1
6725 Xdoor_3, TRUE, FALSE,
6726 EL_EM_GATE_3, -1, -1
6729 Xdoor_4, TRUE, FALSE,
6730 EL_EM_GATE_4, -1, -1
6733 Xdoor_5, TRUE, FALSE,
6734 EL_EMC_GATE_5, -1, -1
6737 Xdoor_6, TRUE, FALSE,
6738 EL_EMC_GATE_6, -1, -1
6741 Xdoor_7, TRUE, FALSE,
6742 EL_EMC_GATE_7, -1, -1
6745 Xdoor_8, TRUE, FALSE,
6746 EL_EMC_GATE_8, -1, -1
6749 Xkey_1, TRUE, FALSE,
6753 Xkey_2, TRUE, FALSE,
6757 Xkey_3, TRUE, FALSE,
6761 Xkey_4, TRUE, FALSE,
6765 Xkey_5, TRUE, FALSE,
6766 EL_EMC_KEY_5, -1, -1
6769 Xkey_6, TRUE, FALSE,
6770 EL_EMC_KEY_6, -1, -1
6773 Xkey_7, TRUE, FALSE,
6774 EL_EMC_KEY_7, -1, -1
6777 Xkey_8, TRUE, FALSE,
6778 EL_EMC_KEY_8, -1, -1
6781 Xwind_n, TRUE, FALSE,
6782 EL_BALLOON_SWITCH_UP, -1, -1
6785 Xwind_e, TRUE, FALSE,
6786 EL_BALLOON_SWITCH_RIGHT, -1, -1
6789 Xwind_s, TRUE, FALSE,
6790 EL_BALLOON_SWITCH_DOWN, -1, -1
6793 Xwind_w, TRUE, FALSE,
6794 EL_BALLOON_SWITCH_LEFT, -1, -1
6797 Xwind_nesw, TRUE, FALSE,
6798 EL_BALLOON_SWITCH_ANY, -1, -1
6801 Xwind_stop, TRUE, FALSE,
6802 EL_BALLOON_SWITCH_NONE, -1, -1
6806 EL_EM_EXIT_CLOSED, -1, -1
6809 Xexit_1, TRUE, FALSE,
6810 EL_EM_EXIT_OPEN, -1, -1
6813 Xexit_2, FALSE, FALSE,
6814 EL_EM_EXIT_OPEN, -1, -1
6817 Xexit_3, FALSE, FALSE,
6818 EL_EM_EXIT_OPEN, -1, -1
6821 Xdynamite, TRUE, FALSE,
6822 EL_EM_DYNAMITE, -1, -1
6825 Ydynamite_eat, FALSE, FALSE,
6826 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6829 Xdynamite_1, TRUE, FALSE,
6830 EL_EM_DYNAMITE_ACTIVE, -1, -1
6833 Xdynamite_2, FALSE, FALSE,
6834 EL_EM_DYNAMITE_ACTIVE, -1, -1
6837 Xdynamite_3, FALSE, FALSE,
6838 EL_EM_DYNAMITE_ACTIVE, -1, -1
6841 Xdynamite_4, FALSE, FALSE,
6842 EL_EM_DYNAMITE_ACTIVE, -1, -1
6845 Xbumper, TRUE, FALSE,
6846 EL_EMC_SPRING_BUMPER, -1, -1
6849 XbumperB, FALSE, FALSE,
6850 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6853 Xwheel, TRUE, FALSE,
6854 EL_ROBOT_WHEEL, -1, -1
6857 XwheelB, FALSE, FALSE,
6858 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6861 Xswitch, TRUE, FALSE,
6862 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6865 XswitchB, FALSE, FALSE,
6866 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6870 EL_QUICKSAND_EMPTY, -1, -1
6873 Xsand_stone, TRUE, FALSE,
6874 EL_QUICKSAND_FULL, -1, -1
6877 Xsand_stonein_1, FALSE, TRUE,
6878 EL_ROCK, ACTION_FILLING, -1
6881 Xsand_stonein_2, FALSE, TRUE,
6882 EL_ROCK, ACTION_FILLING, -1
6885 Xsand_stonein_3, FALSE, TRUE,
6886 EL_ROCK, ACTION_FILLING, -1
6889 Xsand_stonein_4, FALSE, TRUE,
6890 EL_ROCK, ACTION_FILLING, -1
6893 Xsand_stonesand_1, FALSE, FALSE,
6894 EL_QUICKSAND_EMPTYING, -1, -1
6897 Xsand_stonesand_2, FALSE, FALSE,
6898 EL_QUICKSAND_EMPTYING, -1, -1
6901 Xsand_stonesand_3, FALSE, FALSE,
6902 EL_QUICKSAND_EMPTYING, -1, -1
6905 Xsand_stonesand_4, FALSE, FALSE,
6906 EL_QUICKSAND_EMPTYING, -1, -1
6909 Xsand_stonesand_quickout_1, FALSE, FALSE,
6910 EL_QUICKSAND_EMPTYING, -1, -1
6913 Xsand_stonesand_quickout_2, FALSE, FALSE,
6914 EL_QUICKSAND_EMPTYING, -1, -1
6917 Xsand_stoneout_1, FALSE, FALSE,
6918 EL_ROCK, ACTION_EMPTYING, -1
6921 Xsand_stoneout_2, FALSE, FALSE,
6922 EL_ROCK, ACTION_EMPTYING, -1
6925 Xsand_sandstone_1, FALSE, FALSE,
6926 EL_QUICKSAND_FILLING, -1, -1
6929 Xsand_sandstone_2, FALSE, FALSE,
6930 EL_QUICKSAND_FILLING, -1, -1
6933 Xsand_sandstone_3, FALSE, FALSE,
6934 EL_QUICKSAND_FILLING, -1, -1
6937 Xsand_sandstone_4, FALSE, FALSE,
6938 EL_QUICKSAND_FILLING, -1, -1
6941 Xplant, TRUE, FALSE,
6942 EL_EMC_PLANT, -1, -1
6945 Yplant, FALSE, FALSE,
6946 EL_EMC_PLANT, -1, -1
6949 Xlenses, TRUE, FALSE,
6950 EL_EMC_LENSES, -1, -1
6953 Xmagnify, TRUE, FALSE,
6954 EL_EMC_MAGNIFIER, -1, -1
6957 Xdripper, TRUE, FALSE,
6958 EL_EMC_DRIPPER, -1, -1
6961 XdripperB, FALSE, FALSE,
6962 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6965 Xfake_blank, TRUE, FALSE,
6966 EL_INVISIBLE_WALL, -1, -1
6969 Xfake_blankB, FALSE, FALSE,
6970 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6973 Xfake_grass, TRUE, FALSE,
6974 EL_EMC_FAKE_GRASS, -1, -1
6977 Xfake_grassB, FALSE, FALSE,
6978 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6981 Xfake_door_1, TRUE, FALSE,
6982 EL_EM_GATE_1_GRAY, -1, -1
6985 Xfake_door_2, TRUE, FALSE,
6986 EL_EM_GATE_2_GRAY, -1, -1
6989 Xfake_door_3, TRUE, FALSE,
6990 EL_EM_GATE_3_GRAY, -1, -1
6993 Xfake_door_4, TRUE, FALSE,
6994 EL_EM_GATE_4_GRAY, -1, -1
6997 Xfake_door_5, TRUE, FALSE,
6998 EL_EMC_GATE_5_GRAY, -1, -1
7001 Xfake_door_6, TRUE, FALSE,
7002 EL_EMC_GATE_6_GRAY, -1, -1
7005 Xfake_door_7, TRUE, FALSE,
7006 EL_EMC_GATE_7_GRAY, -1, -1
7009 Xfake_door_8, TRUE, FALSE,
7010 EL_EMC_GATE_8_GRAY, -1, -1
7013 Xfake_acid_1, TRUE, FALSE,
7014 EL_EMC_FAKE_ACID, -1, -1
7017 Xfake_acid_2, FALSE, FALSE,
7018 EL_EMC_FAKE_ACID, -1, -1
7021 Xfake_acid_3, FALSE, FALSE,
7022 EL_EMC_FAKE_ACID, -1, -1
7025 Xfake_acid_4, FALSE, FALSE,
7026 EL_EMC_FAKE_ACID, -1, -1
7029 Xfake_acid_5, FALSE, FALSE,
7030 EL_EMC_FAKE_ACID, -1, -1
7033 Xfake_acid_6, FALSE, FALSE,
7034 EL_EMC_FAKE_ACID, -1, -1
7037 Xfake_acid_7, FALSE, FALSE,
7038 EL_EMC_FAKE_ACID, -1, -1
7041 Xfake_acid_8, FALSE, FALSE,
7042 EL_EMC_FAKE_ACID, -1, -1
7045 Xsteel_1, TRUE, FALSE,
7046 EL_STEELWALL, -1, -1
7049 Xsteel_2, TRUE, FALSE,
7050 EL_EMC_STEELWALL_2, -1, -1
7053 Xsteel_3, TRUE, FALSE,
7054 EL_EMC_STEELWALL_3, -1, -1
7057 Xsteel_4, TRUE, FALSE,
7058 EL_EMC_STEELWALL_4, -1, -1
7061 Xwall_1, TRUE, FALSE,
7065 Xwall_2, TRUE, FALSE,
7066 EL_EMC_WALL_14, -1, -1
7069 Xwall_3, TRUE, FALSE,
7070 EL_EMC_WALL_15, -1, -1
7073 Xwall_4, TRUE, FALSE,
7074 EL_EMC_WALL_16, -1, -1
7077 Xround_wall_1, TRUE, FALSE,
7078 EL_WALL_SLIPPERY, -1, -1
7081 Xround_wall_2, TRUE, FALSE,
7082 EL_EMC_WALL_SLIPPERY_2, -1, -1
7085 Xround_wall_3, TRUE, FALSE,
7086 EL_EMC_WALL_SLIPPERY_3, -1, -1
7089 Xround_wall_4, TRUE, FALSE,
7090 EL_EMC_WALL_SLIPPERY_4, -1, -1
7093 Xdecor_1, TRUE, FALSE,
7094 EL_EMC_WALL_8, -1, -1
7097 Xdecor_2, TRUE, FALSE,
7098 EL_EMC_WALL_6, -1, -1
7101 Xdecor_3, TRUE, FALSE,
7102 EL_EMC_WALL_4, -1, -1
7105 Xdecor_4, TRUE, FALSE,
7106 EL_EMC_WALL_7, -1, -1
7109 Xdecor_5, TRUE, FALSE,
7110 EL_EMC_WALL_5, -1, -1
7113 Xdecor_6, TRUE, FALSE,
7114 EL_EMC_WALL_9, -1, -1
7117 Xdecor_7, TRUE, FALSE,
7118 EL_EMC_WALL_10, -1, -1
7121 Xdecor_8, TRUE, FALSE,
7122 EL_EMC_WALL_1, -1, -1
7125 Xdecor_9, TRUE, FALSE,
7126 EL_EMC_WALL_2, -1, -1
7129 Xdecor_10, TRUE, FALSE,
7130 EL_EMC_WALL_3, -1, -1
7133 Xdecor_11, TRUE, FALSE,
7134 EL_EMC_WALL_11, -1, -1
7137 Xdecor_12, TRUE, FALSE,
7138 EL_EMC_WALL_12, -1, -1
7141 Xalpha_0, TRUE, FALSE,
7142 EL_CHAR('0'), -1, -1
7145 Xalpha_1, TRUE, FALSE,
7146 EL_CHAR('1'), -1, -1
7149 Xalpha_2, TRUE, FALSE,
7150 EL_CHAR('2'), -1, -1
7153 Xalpha_3, TRUE, FALSE,
7154 EL_CHAR('3'), -1, -1
7157 Xalpha_4, TRUE, FALSE,
7158 EL_CHAR('4'), -1, -1
7161 Xalpha_5, TRUE, FALSE,
7162 EL_CHAR('5'), -1, -1
7165 Xalpha_6, TRUE, FALSE,
7166 EL_CHAR('6'), -1, -1
7169 Xalpha_7, TRUE, FALSE,
7170 EL_CHAR('7'), -1, -1
7173 Xalpha_8, TRUE, FALSE,
7174 EL_CHAR('8'), -1, -1
7177 Xalpha_9, TRUE, FALSE,
7178 EL_CHAR('9'), -1, -1
7181 Xalpha_excla, TRUE, FALSE,
7182 EL_CHAR('!'), -1, -1
7185 Xalpha_quote, TRUE, FALSE,
7186 EL_CHAR('"'), -1, -1
7189 Xalpha_comma, TRUE, FALSE,
7190 EL_CHAR(','), -1, -1
7193 Xalpha_minus, TRUE, FALSE,
7194 EL_CHAR('-'), -1, -1
7197 Xalpha_perio, TRUE, FALSE,
7198 EL_CHAR('.'), -1, -1
7201 Xalpha_colon, TRUE, FALSE,
7202 EL_CHAR(':'), -1, -1
7205 Xalpha_quest, TRUE, FALSE,
7206 EL_CHAR('?'), -1, -1
7209 Xalpha_a, TRUE, FALSE,
7210 EL_CHAR('A'), -1, -1
7213 Xalpha_b, TRUE, FALSE,
7214 EL_CHAR('B'), -1, -1
7217 Xalpha_c, TRUE, FALSE,
7218 EL_CHAR('C'), -1, -1
7221 Xalpha_d, TRUE, FALSE,
7222 EL_CHAR('D'), -1, -1
7225 Xalpha_e, TRUE, FALSE,
7226 EL_CHAR('E'), -1, -1
7229 Xalpha_f, TRUE, FALSE,
7230 EL_CHAR('F'), -1, -1
7233 Xalpha_g, TRUE, FALSE,
7234 EL_CHAR('G'), -1, -1
7237 Xalpha_h, TRUE, FALSE,
7238 EL_CHAR('H'), -1, -1
7241 Xalpha_i, TRUE, FALSE,
7242 EL_CHAR('I'), -1, -1
7245 Xalpha_j, TRUE, FALSE,
7246 EL_CHAR('J'), -1, -1
7249 Xalpha_k, TRUE, FALSE,
7250 EL_CHAR('K'), -1, -1
7253 Xalpha_l, TRUE, FALSE,
7254 EL_CHAR('L'), -1, -1
7257 Xalpha_m, TRUE, FALSE,
7258 EL_CHAR('M'), -1, -1
7261 Xalpha_n, TRUE, FALSE,
7262 EL_CHAR('N'), -1, -1
7265 Xalpha_o, TRUE, FALSE,
7266 EL_CHAR('O'), -1, -1
7269 Xalpha_p, TRUE, FALSE,
7270 EL_CHAR('P'), -1, -1
7273 Xalpha_q, TRUE, FALSE,
7274 EL_CHAR('Q'), -1, -1
7277 Xalpha_r, TRUE, FALSE,
7278 EL_CHAR('R'), -1, -1
7281 Xalpha_s, TRUE, FALSE,
7282 EL_CHAR('S'), -1, -1
7285 Xalpha_t, TRUE, FALSE,
7286 EL_CHAR('T'), -1, -1
7289 Xalpha_u, TRUE, FALSE,
7290 EL_CHAR('U'), -1, -1
7293 Xalpha_v, TRUE, FALSE,
7294 EL_CHAR('V'), -1, -1
7297 Xalpha_w, TRUE, FALSE,
7298 EL_CHAR('W'), -1, -1
7301 Xalpha_x, TRUE, FALSE,
7302 EL_CHAR('X'), -1, -1
7305 Xalpha_y, TRUE, FALSE,
7306 EL_CHAR('Y'), -1, -1
7309 Xalpha_z, TRUE, FALSE,
7310 EL_CHAR('Z'), -1, -1
7313 Xalpha_arrow_e, TRUE, FALSE,
7314 EL_CHAR('>'), -1, -1
7317 Xalpha_arrow_w, TRUE, FALSE,
7318 EL_CHAR('<'), -1, -1
7321 Xalpha_copyr, TRUE, FALSE,
7322 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7326 Xboom_bug, FALSE, FALSE,
7327 EL_BUG, ACTION_EXPLODING, -1
7330 Xboom_bomb, FALSE, FALSE,
7331 EL_BOMB, ACTION_EXPLODING, -1
7334 Xboom_android, FALSE, FALSE,
7335 EL_EMC_ANDROID, ACTION_OTHER, -1
7338 Xboom_1, FALSE, FALSE,
7339 EL_DEFAULT, ACTION_EXPLODING, -1
7342 Xboom_2, FALSE, FALSE,
7343 EL_DEFAULT, ACTION_EXPLODING, -1
7346 Znormal, FALSE, FALSE,
7350 Zdynamite, FALSE, FALSE,
7354 Zplayer, FALSE, FALSE,
7358 ZBORDER, FALSE, FALSE,
7368 static struct Mapping_EM_to_RND_player
7377 em_player_mapping_list[] =
7381 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7385 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7389 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7393 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7397 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7401 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7405 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7409 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7413 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7417 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7421 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7425 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7429 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7433 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7437 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7441 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7445 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7449 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7453 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7457 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7461 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7465 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7469 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7473 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7477 EL_PLAYER_1, ACTION_DEFAULT, -1,
7481 EL_PLAYER_2, ACTION_DEFAULT, -1,
7485 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7489 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7493 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7497 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7501 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7505 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7509 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7513 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7517 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7521 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7525 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7529 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7533 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7537 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7541 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7545 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7549 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7553 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7557 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7561 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7565 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7569 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7573 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7577 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7581 EL_PLAYER_3, ACTION_DEFAULT, -1,
7585 EL_PLAYER_4, ACTION_DEFAULT, -1,
7594 int map_element_RND_to_EM(int element_rnd)
7596 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7597 static boolean mapping_initialized = FALSE;
7599 if (!mapping_initialized)
7603 // return "Xalpha_quest" for all undefined elements in mapping array
7604 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7605 mapping_RND_to_EM[i] = Xalpha_quest;
7607 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7608 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7609 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7610 em_object_mapping_list[i].element_em;
7612 mapping_initialized = TRUE;
7615 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7616 return mapping_RND_to_EM[element_rnd];
7618 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7623 int map_element_EM_to_RND(int element_em)
7625 static unsigned short mapping_EM_to_RND[TILE_MAX];
7626 static boolean mapping_initialized = FALSE;
7628 if (!mapping_initialized)
7632 // return "EL_UNKNOWN" for all undefined elements in mapping array
7633 for (i = 0; i < TILE_MAX; i++)
7634 mapping_EM_to_RND[i] = EL_UNKNOWN;
7636 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7637 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7638 em_object_mapping_list[i].element_rnd;
7640 mapping_initialized = TRUE;
7643 if (element_em >= 0 && element_em < TILE_MAX)
7644 return mapping_EM_to_RND[element_em];
7646 Error(ERR_WARN, "invalid EM level element %d", element_em);
7651 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7653 struct LevelInfo_EM *level_em = level->native_em_level;
7654 struct LEVEL *lev = level_em->lev;
7657 for (i = 0; i < TILE_MAX; i++)
7658 lev->android_array[i] = Xblank;
7660 for (i = 0; i < level->num_android_clone_elements; i++)
7662 int element_rnd = level->android_clone_element[i];
7663 int element_em = map_element_RND_to_EM(element_rnd);
7665 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7666 if (em_object_mapping_list[j].element_rnd == element_rnd)
7667 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7671 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7673 struct LevelInfo_EM *level_em = level->native_em_level;
7674 struct LEVEL *lev = level_em->lev;
7677 level->num_android_clone_elements = 0;
7679 for (i = 0; i < TILE_MAX; i++)
7681 int element_em = lev->android_array[i];
7683 boolean element_found = FALSE;
7685 if (element_em == Xblank)
7688 element_rnd = map_element_EM_to_RND(element_em);
7690 for (j = 0; j < level->num_android_clone_elements; j++)
7691 if (level->android_clone_element[j] == element_rnd)
7692 element_found = TRUE;
7696 level->android_clone_element[level->num_android_clone_elements++] =
7699 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7704 if (level->num_android_clone_elements == 0)
7706 level->num_android_clone_elements = 1;
7707 level->android_clone_element[0] = EL_EMPTY;
7711 int map_direction_RND_to_EM(int direction)
7713 return (direction == MV_UP ? 0 :
7714 direction == MV_RIGHT ? 1 :
7715 direction == MV_DOWN ? 2 :
7716 direction == MV_LEFT ? 3 :
7720 int map_direction_EM_to_RND(int direction)
7722 return (direction == 0 ? MV_UP :
7723 direction == 1 ? MV_RIGHT :
7724 direction == 2 ? MV_DOWN :
7725 direction == 3 ? MV_LEFT :
7729 int map_element_RND_to_SP(int element_rnd)
7731 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7733 if (element_rnd >= EL_SP_START &&
7734 element_rnd <= EL_SP_END)
7735 element_sp = element_rnd - EL_SP_START;
7736 else if (element_rnd == EL_EMPTY_SPACE)
7738 else if (element_rnd == EL_INVISIBLE_WALL)
7744 int map_element_SP_to_RND(int element_sp)
7746 int element_rnd = EL_UNKNOWN;
7748 if (element_sp >= 0x00 &&
7750 element_rnd = EL_SP_START + element_sp;
7751 else if (element_sp == 0x28)
7752 element_rnd = EL_INVISIBLE_WALL;
7757 int map_action_SP_to_RND(int action_sp)
7761 case actActive: return ACTION_ACTIVE;
7762 case actImpact: return ACTION_IMPACT;
7763 case actExploding: return ACTION_EXPLODING;
7764 case actDigging: return ACTION_DIGGING;
7765 case actSnapping: return ACTION_SNAPPING;
7766 case actCollecting: return ACTION_COLLECTING;
7767 case actPassing: return ACTION_PASSING;
7768 case actPushing: return ACTION_PUSHING;
7769 case actDropping: return ACTION_DROPPING;
7771 default: return ACTION_DEFAULT;
7775 int map_element_RND_to_MM(int element_rnd)
7777 return (element_rnd >= EL_MM_START_1 &&
7778 element_rnd <= EL_MM_END_1 ?
7779 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7781 element_rnd >= EL_MM_START_2 &&
7782 element_rnd <= EL_MM_END_2 ?
7783 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7785 element_rnd >= EL_CHAR_START &&
7786 element_rnd <= EL_CHAR_END ?
7787 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7789 element_rnd >= EL_MM_RUNTIME_START &&
7790 element_rnd <= EL_MM_RUNTIME_END ?
7791 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7793 element_rnd >= EL_MM_DUMMY_START &&
7794 element_rnd <= EL_MM_DUMMY_END ?
7795 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7797 EL_MM_EMPTY_NATIVE);
7800 int map_element_MM_to_RND(int element_mm)
7802 return (element_mm == EL_MM_EMPTY_NATIVE ||
7803 element_mm == EL_DF_EMPTY_NATIVE ?
7806 element_mm >= EL_MM_START_1_NATIVE &&
7807 element_mm <= EL_MM_END_1_NATIVE ?
7808 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7810 element_mm >= EL_MM_START_2_NATIVE &&
7811 element_mm <= EL_MM_END_2_NATIVE ?
7812 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7814 element_mm >= EL_MM_CHAR_START_NATIVE &&
7815 element_mm <= EL_MM_CHAR_END_NATIVE ?
7816 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7818 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7819 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7820 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7822 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7823 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7824 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7829 int map_action_MM_to_RND(int action_mm)
7831 // all MM actions are defined to exactly match their RND counterparts
7835 int map_sound_MM_to_RND(int sound_mm)
7839 case SND_MM_GAME_LEVELTIME_CHARGING:
7840 return SND_GAME_LEVELTIME_CHARGING;
7842 case SND_MM_GAME_HEALTH_CHARGING:
7843 return SND_GAME_HEALTH_CHARGING;
7846 return SND_UNDEFINED;
7850 int map_mm_wall_element(int element)
7852 return (element >= EL_MM_STEEL_WALL_START &&
7853 element <= EL_MM_STEEL_WALL_END ?
7856 element >= EL_MM_WOODEN_WALL_START &&
7857 element <= EL_MM_WOODEN_WALL_END ?
7860 element >= EL_MM_ICE_WALL_START &&
7861 element <= EL_MM_ICE_WALL_END ?
7864 element >= EL_MM_AMOEBA_WALL_START &&
7865 element <= EL_MM_AMOEBA_WALL_END ?
7868 element >= EL_DF_STEEL_WALL_START &&
7869 element <= EL_DF_STEEL_WALL_END ?
7872 element >= EL_DF_WOODEN_WALL_START &&
7873 element <= EL_DF_WOODEN_WALL_END ?
7879 int map_mm_wall_element_editor(int element)
7883 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7884 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7885 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7886 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7887 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7888 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7890 default: return element;
7894 int get_next_element(int element)
7898 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7899 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7900 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7901 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7902 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7903 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7904 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7905 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7906 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7907 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7908 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7910 default: return element;
7914 int el2img_mm(int element_mm)
7916 return el2img(map_element_MM_to_RND(element_mm));
7919 int el_act_dir2img(int element, int action, int direction)
7921 element = GFX_ELEMENT(element);
7922 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7924 // direction_graphic[][] == graphic[] for undefined direction graphics
7925 return element_info[element].direction_graphic[action][direction];
7928 static int el_act_dir2crm(int element, int action, int direction)
7930 element = GFX_ELEMENT(element);
7931 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7933 // direction_graphic[][] == graphic[] for undefined direction graphics
7934 return element_info[element].direction_crumbled[action][direction];
7937 int el_act2img(int element, int action)
7939 element = GFX_ELEMENT(element);
7941 return element_info[element].graphic[action];
7944 int el_act2crm(int element, int action)
7946 element = GFX_ELEMENT(element);
7948 return element_info[element].crumbled[action];
7951 int el_dir2img(int element, int direction)
7953 element = GFX_ELEMENT(element);
7955 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7958 int el2baseimg(int element)
7960 return element_info[element].graphic[ACTION_DEFAULT];
7963 int el2img(int element)
7965 element = GFX_ELEMENT(element);
7967 return element_info[element].graphic[ACTION_DEFAULT];
7970 int el2edimg(int element)
7972 element = GFX_ELEMENT(element);
7974 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7977 int el2preimg(int element)
7979 element = GFX_ELEMENT(element);
7981 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7984 int el2panelimg(int element)
7986 element = GFX_ELEMENT(element);
7988 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7991 int font2baseimg(int font_nr)
7993 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7996 int getBeltNrFromBeltElement(int element)
7998 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7999 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8000 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8003 int getBeltNrFromBeltActiveElement(int element)
8005 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8006 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8007 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8010 int getBeltNrFromBeltSwitchElement(int element)
8012 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8013 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8014 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8017 int getBeltDirNrFromBeltElement(int element)
8019 static int belt_base_element[4] =
8021 EL_CONVEYOR_BELT_1_LEFT,
8022 EL_CONVEYOR_BELT_2_LEFT,
8023 EL_CONVEYOR_BELT_3_LEFT,
8024 EL_CONVEYOR_BELT_4_LEFT
8027 int belt_nr = getBeltNrFromBeltElement(element);
8028 int belt_dir_nr = element - belt_base_element[belt_nr];
8030 return (belt_dir_nr % 3);
8033 int getBeltDirNrFromBeltSwitchElement(int element)
8035 static int belt_base_element[4] =
8037 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8038 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8039 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8040 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8043 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8044 int belt_dir_nr = element - belt_base_element[belt_nr];
8046 return (belt_dir_nr % 3);
8049 int getBeltDirFromBeltElement(int element)
8051 static int belt_move_dir[3] =
8058 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8060 return belt_move_dir[belt_dir_nr];
8063 int getBeltDirFromBeltSwitchElement(int element)
8065 static int belt_move_dir[3] =
8072 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8074 return belt_move_dir[belt_dir_nr];
8077 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8079 static int belt_base_element[4] =
8081 EL_CONVEYOR_BELT_1_LEFT,
8082 EL_CONVEYOR_BELT_2_LEFT,
8083 EL_CONVEYOR_BELT_3_LEFT,
8084 EL_CONVEYOR_BELT_4_LEFT
8087 return belt_base_element[belt_nr] + belt_dir_nr;
8090 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8092 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8094 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8097 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8099 static int belt_base_element[4] =
8101 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8102 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8103 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8104 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8107 return belt_base_element[belt_nr] + belt_dir_nr;
8110 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8112 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8114 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8117 boolean getTeamMode_EM(void)
8119 return game.team_mode || network_playing;
8122 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8124 int game_frame_delay_value;
8126 game_frame_delay_value =
8127 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8128 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8131 if (tape.playing && tape.warp_forward && !tape.pausing)
8132 game_frame_delay_value = 0;
8134 return game_frame_delay_value;
8137 unsigned int InitRND(int seed)
8139 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8140 return InitEngineRandom_EM(seed);
8141 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8142 return InitEngineRandom_SP(seed);
8143 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8144 return InitEngineRandom_MM(seed);
8146 return InitEngineRandom_RND(seed);
8149 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8150 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8152 static int get_effective_element_EM(int tile, int frame_em)
8154 int element = object_mapping[tile].element_rnd;
8155 int action = object_mapping[tile].action;
8156 boolean is_backside = object_mapping[tile].is_backside;
8157 boolean action_removing = (action == ACTION_DIGGING ||
8158 action == ACTION_SNAPPING ||
8159 action == ACTION_COLLECTING);
8165 case Yacid_splash_eB:
8166 case Yacid_splash_wB:
8167 return (frame_em > 5 ? EL_EMPTY : element);
8173 else // frame_em == 7
8177 case Yacid_splash_eB:
8178 case Yacid_splash_wB:
8181 case Yemerald_stone:
8184 case Ydiamond_stone:
8188 case Xdrip_stretchB:
8207 case Xsand_stonein_1:
8208 case Xsand_stonein_2:
8209 case Xsand_stonein_3:
8210 case Xsand_stonein_4:
8214 return (is_backside || action_removing ? EL_EMPTY : element);
8219 static boolean check_linear_animation_EM(int tile)
8223 case Xsand_stonesand_1:
8224 case Xsand_stonesand_quickout_1:
8225 case Xsand_sandstone_1:
8226 case Xsand_stonein_1:
8227 case Xsand_stoneout_1:
8246 case Yacid_splash_eB:
8247 case Yacid_splash_wB:
8248 case Yemerald_stone:
8255 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8256 boolean has_crumbled_graphics,
8257 int crumbled, int sync_frame)
8259 // if element can be crumbled, but certain action graphics are just empty
8260 // space (like instantly snapping sand to empty space in 1 frame), do not
8261 // treat these empty space graphics as crumbled graphics in EMC engine
8262 if (crumbled == IMG_EMPTY_SPACE)
8263 has_crumbled_graphics = FALSE;
8265 if (has_crumbled_graphics)
8267 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8268 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8269 g_crumbled->anim_delay,
8270 g_crumbled->anim_mode,
8271 g_crumbled->anim_start_frame,
8274 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8275 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8277 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8278 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8280 g_em->has_crumbled_graphics = TRUE;
8284 g_em->crumbled_bitmap = NULL;
8285 g_em->crumbled_src_x = 0;
8286 g_em->crumbled_src_y = 0;
8287 g_em->crumbled_border_size = 0;
8288 g_em->crumbled_tile_size = 0;
8290 g_em->has_crumbled_graphics = FALSE;
8295 void ResetGfxAnimation_EM(int x, int y, int tile)
8301 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8302 int tile, int frame_em, int x, int y)
8304 int action = object_mapping[tile].action;
8305 int direction = object_mapping[tile].direction;
8306 int effective_element = get_effective_element_EM(tile, frame_em);
8307 int graphic = (direction == MV_NONE ?
8308 el_act2img(effective_element, action) :
8309 el_act_dir2img(effective_element, action, direction));
8310 struct GraphicInfo *g = &graphic_info[graphic];
8312 boolean action_removing = (action == ACTION_DIGGING ||
8313 action == ACTION_SNAPPING ||
8314 action == ACTION_COLLECTING);
8315 boolean action_moving = (action == ACTION_FALLING ||
8316 action == ACTION_MOVING ||
8317 action == ACTION_PUSHING ||
8318 action == ACTION_EATING ||
8319 action == ACTION_FILLING ||
8320 action == ACTION_EMPTYING);
8321 boolean action_falling = (action == ACTION_FALLING ||
8322 action == ACTION_FILLING ||
8323 action == ACTION_EMPTYING);
8325 // special case: graphic uses "2nd movement tile" and has defined
8326 // 7 frames for movement animation (or less) => use default graphic
8327 // for last (8th) frame which ends the movement animation
8328 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8330 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8331 graphic = (direction == MV_NONE ?
8332 el_act2img(effective_element, action) :
8333 el_act_dir2img(effective_element, action, direction));
8335 g = &graphic_info[graphic];
8338 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8342 else if (action_moving)
8344 boolean is_backside = object_mapping[tile].is_backside;
8348 int direction = object_mapping[tile].direction;
8349 int move_dir = (action_falling ? MV_DOWN : direction);
8354 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8355 if (g->double_movement && frame_em == 0)
8359 if (move_dir == MV_LEFT)
8360 GfxFrame[x - 1][y] = GfxFrame[x][y];
8361 else if (move_dir == MV_RIGHT)
8362 GfxFrame[x + 1][y] = GfxFrame[x][y];
8363 else if (move_dir == MV_UP)
8364 GfxFrame[x][y - 1] = GfxFrame[x][y];
8365 else if (move_dir == MV_DOWN)
8366 GfxFrame[x][y + 1] = GfxFrame[x][y];
8373 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8374 if (tile == Xsand_stonesand_quickout_1 ||
8375 tile == Xsand_stonesand_quickout_2)
8379 if (graphic_info[graphic].anim_global_sync)
8380 sync_frame = FrameCounter;
8381 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8382 sync_frame = GfxFrame[x][y];
8384 sync_frame = 0; // playfield border (pseudo steel)
8386 SetRandomAnimationValue(x, y);
8388 int frame = getAnimationFrame(g->anim_frames,
8391 g->anim_start_frame,
8394 g_em->unique_identifier =
8395 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8398 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8399 int tile, int frame_em, int x, int y)
8401 int action = object_mapping[tile].action;
8402 int direction = object_mapping[tile].direction;
8403 boolean is_backside = object_mapping[tile].is_backside;
8404 int effective_element = get_effective_element_EM(tile, frame_em);
8405 int effective_action = action;
8406 int graphic = (direction == MV_NONE ?
8407 el_act2img(effective_element, effective_action) :
8408 el_act_dir2img(effective_element, effective_action,
8410 int crumbled = (direction == MV_NONE ?
8411 el_act2crm(effective_element, effective_action) :
8412 el_act_dir2crm(effective_element, effective_action,
8414 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8415 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8416 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8417 struct GraphicInfo *g = &graphic_info[graphic];
8420 // special case: graphic uses "2nd movement tile" and has defined
8421 // 7 frames for movement animation (or less) => use default graphic
8422 // for last (8th) frame which ends the movement animation
8423 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8425 effective_action = ACTION_DEFAULT;
8426 graphic = (direction == MV_NONE ?
8427 el_act2img(effective_element, effective_action) :
8428 el_act_dir2img(effective_element, effective_action,
8430 crumbled = (direction == MV_NONE ?
8431 el_act2crm(effective_element, effective_action) :
8432 el_act_dir2crm(effective_element, effective_action,
8435 g = &graphic_info[graphic];
8438 if (graphic_info[graphic].anim_global_sync)
8439 sync_frame = FrameCounter;
8440 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8441 sync_frame = GfxFrame[x][y];
8443 sync_frame = 0; // playfield border (pseudo steel)
8445 SetRandomAnimationValue(x, y);
8447 int frame = getAnimationFrame(g->anim_frames,
8450 g->anim_start_frame,
8453 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8454 g->double_movement && is_backside);
8456 // (updating the "crumbled" graphic definitions is probably not really needed,
8457 // as animations for crumbled graphics can't be longer than one EMC cycle)
8458 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8462 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8463 int player_nr, int anim, int frame_em)
8465 int element = player_mapping[player_nr][anim].element_rnd;
8466 int action = player_mapping[player_nr][anim].action;
8467 int direction = player_mapping[player_nr][anim].direction;
8468 int graphic = (direction == MV_NONE ?
8469 el_act2img(element, action) :
8470 el_act_dir2img(element, action, direction));
8471 struct GraphicInfo *g = &graphic_info[graphic];
8474 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8476 stored_player[player_nr].StepFrame = frame_em;
8478 sync_frame = stored_player[player_nr].Frame;
8480 int frame = getAnimationFrame(g->anim_frames,
8483 g->anim_start_frame,
8486 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8487 &g_em->src_x, &g_em->src_y, FALSE);
8490 void InitGraphicInfo_EM(void)
8495 int num_em_gfx_errors = 0;
8497 if (graphic_info_em_object[0][0].bitmap == NULL)
8499 // EM graphics not yet initialized in em_open_all()
8504 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8507 // always start with reliable default values
8508 for (i = 0; i < TILE_MAX; i++)
8510 object_mapping[i].element_rnd = EL_UNKNOWN;
8511 object_mapping[i].is_backside = FALSE;
8512 object_mapping[i].action = ACTION_DEFAULT;
8513 object_mapping[i].direction = MV_NONE;
8516 // always start with reliable default values
8517 for (p = 0; p < MAX_PLAYERS; p++)
8519 for (i = 0; i < SPR_MAX; i++)
8521 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8522 player_mapping[p][i].action = ACTION_DEFAULT;
8523 player_mapping[p][i].direction = MV_NONE;
8527 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8529 int e = em_object_mapping_list[i].element_em;
8531 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8532 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8534 if (em_object_mapping_list[i].action != -1)
8535 object_mapping[e].action = em_object_mapping_list[i].action;
8537 if (em_object_mapping_list[i].direction != -1)
8538 object_mapping[e].direction =
8539 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8542 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8544 int a = em_player_mapping_list[i].action_em;
8545 int p = em_player_mapping_list[i].player_nr;
8547 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8549 if (em_player_mapping_list[i].action != -1)
8550 player_mapping[p][a].action = em_player_mapping_list[i].action;
8552 if (em_player_mapping_list[i].direction != -1)
8553 player_mapping[p][a].direction =
8554 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8557 for (i = 0; i < TILE_MAX; i++)
8559 int element = object_mapping[i].element_rnd;
8560 int action = object_mapping[i].action;
8561 int direction = object_mapping[i].direction;
8562 boolean is_backside = object_mapping[i].is_backside;
8563 boolean action_exploding = ((action == ACTION_EXPLODING ||
8564 action == ACTION_SMASHED_BY_ROCK ||
8565 action == ACTION_SMASHED_BY_SPRING) &&
8566 element != EL_DIAMOND);
8567 boolean action_active = (action == ACTION_ACTIVE);
8568 boolean action_other = (action == ACTION_OTHER);
8570 for (j = 0; j < 8; j++)
8572 int effective_element = get_effective_element_EM(i, j);
8573 int effective_action = (j < 7 ? action :
8574 i == Xdrip_stretch ? action :
8575 i == Xdrip_stretchB ? action :
8576 i == Ydrip_s1 ? action :
8577 i == Ydrip_s1B ? action :
8578 i == Xball_1B ? action :
8579 i == Xball_2 ? action :
8580 i == Xball_2B ? action :
8581 i == Yball_eat ? action :
8582 i == Ykey_1_eat ? action :
8583 i == Ykey_2_eat ? action :
8584 i == Ykey_3_eat ? action :
8585 i == Ykey_4_eat ? action :
8586 i == Ykey_5_eat ? action :
8587 i == Ykey_6_eat ? action :
8588 i == Ykey_7_eat ? action :
8589 i == Ykey_8_eat ? action :
8590 i == Ylenses_eat ? action :
8591 i == Ymagnify_eat ? action :
8592 i == Ygrass_eat ? action :
8593 i == Ydirt_eat ? action :
8594 i == Xsand_stonein_1 ? action :
8595 i == Xsand_stonein_2 ? action :
8596 i == Xsand_stonein_3 ? action :
8597 i == Xsand_stonein_4 ? action :
8598 i == Xsand_stoneout_1 ? action :
8599 i == Xsand_stoneout_2 ? action :
8600 i == Xboom_android ? ACTION_EXPLODING :
8601 action_exploding ? ACTION_EXPLODING :
8602 action_active ? action :
8603 action_other ? action :
8605 int graphic = (el_act_dir2img(effective_element, effective_action,
8607 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8609 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8610 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8611 boolean has_action_graphics = (graphic != base_graphic);
8612 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8613 struct GraphicInfo *g = &graphic_info[graphic];
8614 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8617 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8618 boolean special_animation = (action != ACTION_DEFAULT &&
8619 g->anim_frames == 3 &&
8620 g->anim_delay == 2 &&
8621 g->anim_mode & ANIM_LINEAR);
8622 int sync_frame = (i == Xdrip_stretch ? 7 :
8623 i == Xdrip_stretchB ? 7 :
8624 i == Ydrip_s2 ? j + 8 :
8625 i == Ydrip_s2B ? j + 8 :
8634 i == Xfake_acid_1 ? 0 :
8635 i == Xfake_acid_2 ? 10 :
8636 i == Xfake_acid_3 ? 20 :
8637 i == Xfake_acid_4 ? 30 :
8638 i == Xfake_acid_5 ? 40 :
8639 i == Xfake_acid_6 ? 50 :
8640 i == Xfake_acid_7 ? 60 :
8641 i == Xfake_acid_8 ? 70 :
8643 i == Xball_2B ? j + 8 :
8644 i == Yball_eat ? j + 1 :
8645 i == Ykey_1_eat ? j + 1 :
8646 i == Ykey_2_eat ? j + 1 :
8647 i == Ykey_3_eat ? j + 1 :
8648 i == Ykey_4_eat ? j + 1 :
8649 i == Ykey_5_eat ? j + 1 :
8650 i == Ykey_6_eat ? j + 1 :
8651 i == Ykey_7_eat ? j + 1 :
8652 i == Ykey_8_eat ? j + 1 :
8653 i == Ylenses_eat ? j + 1 :
8654 i == Ymagnify_eat ? j + 1 :
8655 i == Ygrass_eat ? j + 1 :
8656 i == Ydirt_eat ? j + 1 :
8657 i == Xamoeba_1 ? 0 :
8658 i == Xamoeba_2 ? 1 :
8659 i == Xamoeba_3 ? 2 :
8660 i == Xamoeba_4 ? 3 :
8661 i == Xamoeba_5 ? 0 :
8662 i == Xamoeba_6 ? 1 :
8663 i == Xamoeba_7 ? 2 :
8664 i == Xamoeba_8 ? 3 :
8665 i == Xexit_2 ? j + 8 :
8666 i == Xexit_3 ? j + 16 :
8667 i == Xdynamite_1 ? 0 :
8668 i == Xdynamite_2 ? 8 :
8669 i == Xdynamite_3 ? 16 :
8670 i == Xdynamite_4 ? 24 :
8671 i == Xsand_stonein_1 ? j + 1 :
8672 i == Xsand_stonein_2 ? j + 9 :
8673 i == Xsand_stonein_3 ? j + 17 :
8674 i == Xsand_stonein_4 ? j + 25 :
8675 i == Xsand_stoneout_1 && j == 0 ? 0 :
8676 i == Xsand_stoneout_1 && j == 1 ? 0 :
8677 i == Xsand_stoneout_1 && j == 2 ? 1 :
8678 i == Xsand_stoneout_1 && j == 3 ? 2 :
8679 i == Xsand_stoneout_1 && j == 4 ? 2 :
8680 i == Xsand_stoneout_1 && j == 5 ? 3 :
8681 i == Xsand_stoneout_1 && j == 6 ? 4 :
8682 i == Xsand_stoneout_1 && j == 7 ? 4 :
8683 i == Xsand_stoneout_2 && j == 0 ? 5 :
8684 i == Xsand_stoneout_2 && j == 1 ? 6 :
8685 i == Xsand_stoneout_2 && j == 2 ? 7 :
8686 i == Xsand_stoneout_2 && j == 3 ? 8 :
8687 i == Xsand_stoneout_2 && j == 4 ? 9 :
8688 i == Xsand_stoneout_2 && j == 5 ? 11 :
8689 i == Xsand_stoneout_2 && j == 6 ? 13 :
8690 i == Xsand_stoneout_2 && j == 7 ? 15 :
8691 i == Xboom_bug && j == 1 ? 2 :
8692 i == Xboom_bug && j == 2 ? 2 :
8693 i == Xboom_bug && j == 3 ? 4 :
8694 i == Xboom_bug && j == 4 ? 4 :
8695 i == Xboom_bug && j == 5 ? 2 :
8696 i == Xboom_bug && j == 6 ? 2 :
8697 i == Xboom_bug && j == 7 ? 0 :
8698 i == Xboom_bomb && j == 1 ? 2 :
8699 i == Xboom_bomb && j == 2 ? 2 :
8700 i == Xboom_bomb && j == 3 ? 4 :
8701 i == Xboom_bomb && j == 4 ? 4 :
8702 i == Xboom_bomb && j == 5 ? 2 :
8703 i == Xboom_bomb && j == 6 ? 2 :
8704 i == Xboom_bomb && j == 7 ? 0 :
8705 i == Xboom_android && j == 7 ? 6 :
8706 i == Xboom_1 && j == 1 ? 2 :
8707 i == Xboom_1 && j == 2 ? 2 :
8708 i == Xboom_1 && j == 3 ? 4 :
8709 i == Xboom_1 && j == 4 ? 4 :
8710 i == Xboom_1 && j == 5 ? 6 :
8711 i == Xboom_1 && j == 6 ? 6 :
8712 i == Xboom_1 && j == 7 ? 8 :
8713 i == Xboom_2 && j == 0 ? 8 :
8714 i == Xboom_2 && j == 1 ? 8 :
8715 i == Xboom_2 && j == 2 ? 10 :
8716 i == Xboom_2 && j == 3 ? 10 :
8717 i == Xboom_2 && j == 4 ? 10 :
8718 i == Xboom_2 && j == 5 ? 12 :
8719 i == Xboom_2 && j == 6 ? 12 :
8720 i == Xboom_2 && j == 7 ? 12 :
8721 special_animation && j == 4 ? 3 :
8722 effective_action != action ? 0 :
8726 Bitmap *debug_bitmap = g_em->bitmap;
8727 int debug_src_x = g_em->src_x;
8728 int debug_src_y = g_em->src_y;
8731 int frame = getAnimationFrame(g->anim_frames,
8734 g->anim_start_frame,
8737 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8738 g->double_movement && is_backside);
8740 g_em->bitmap = src_bitmap;
8741 g_em->src_x = src_x;
8742 g_em->src_y = src_y;
8743 g_em->src_offset_x = 0;
8744 g_em->src_offset_y = 0;
8745 g_em->dst_offset_x = 0;
8746 g_em->dst_offset_y = 0;
8747 g_em->width = TILEX;
8748 g_em->height = TILEY;
8750 g_em->preserve_background = FALSE;
8752 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8755 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8756 effective_action == ACTION_MOVING ||
8757 effective_action == ACTION_PUSHING ||
8758 effective_action == ACTION_EATING)) ||
8759 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8760 effective_action == ACTION_EMPTYING)))
8763 (effective_action == ACTION_FALLING ||
8764 effective_action == ACTION_FILLING ||
8765 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8766 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8767 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8768 int num_steps = (i == Ydrip_s1 ? 16 :
8769 i == Ydrip_s1B ? 16 :
8770 i == Ydrip_s2 ? 16 :
8771 i == Ydrip_s2B ? 16 :
8772 i == Xsand_stonein_1 ? 32 :
8773 i == Xsand_stonein_2 ? 32 :
8774 i == Xsand_stonein_3 ? 32 :
8775 i == Xsand_stonein_4 ? 32 :
8776 i == Xsand_stoneout_1 ? 16 :
8777 i == Xsand_stoneout_2 ? 16 : 8);
8778 int cx = ABS(dx) * (TILEX / num_steps);
8779 int cy = ABS(dy) * (TILEY / num_steps);
8780 int step_frame = (i == Ydrip_s2 ? j + 8 :
8781 i == Ydrip_s2B ? j + 8 :
8782 i == Xsand_stonein_2 ? j + 8 :
8783 i == Xsand_stonein_3 ? j + 16 :
8784 i == Xsand_stonein_4 ? j + 24 :
8785 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8786 int step = (is_backside ? step_frame : num_steps - step_frame);
8788 if (is_backside) // tile where movement starts
8790 if (dx < 0 || dy < 0)
8792 g_em->src_offset_x = cx * step;
8793 g_em->src_offset_y = cy * step;
8797 g_em->dst_offset_x = cx * step;
8798 g_em->dst_offset_y = cy * step;
8801 else // tile where movement ends
8803 if (dx < 0 || dy < 0)
8805 g_em->dst_offset_x = cx * step;
8806 g_em->dst_offset_y = cy * step;
8810 g_em->src_offset_x = cx * step;
8811 g_em->src_offset_y = cy * step;
8815 g_em->width = TILEX - cx * step;
8816 g_em->height = TILEY - cy * step;
8819 // create unique graphic identifier to decide if tile must be redrawn
8820 /* bit 31 - 16 (16 bit): EM style graphic
8821 bit 15 - 12 ( 4 bit): EM style frame
8822 bit 11 - 6 ( 6 bit): graphic width
8823 bit 5 - 0 ( 6 bit): graphic height */
8824 g_em->unique_identifier =
8825 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8829 // skip check for EMC elements not contained in original EMC artwork
8830 if (element == EL_EMC_FAKE_ACID)
8833 if (g_em->bitmap != debug_bitmap ||
8834 g_em->src_x != debug_src_x ||
8835 g_em->src_y != debug_src_y ||
8836 g_em->src_offset_x != 0 ||
8837 g_em->src_offset_y != 0 ||
8838 g_em->dst_offset_x != 0 ||
8839 g_em->dst_offset_y != 0 ||
8840 g_em->width != TILEX ||
8841 g_em->height != TILEY)
8843 static int last_i = -1;
8851 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8852 i, element, element_info[element].token_name,
8853 element_action_info[effective_action].suffix, direction);
8855 if (element != effective_element)
8856 printf(" [%d ('%s')]",
8858 element_info[effective_element].token_name);
8862 if (g_em->bitmap != debug_bitmap)
8863 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8864 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8866 if (g_em->src_x != debug_src_x ||
8867 g_em->src_y != debug_src_y)
8868 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8869 j, (is_backside ? 'B' : 'F'),
8870 g_em->src_x, g_em->src_y,
8871 g_em->src_x / 32, g_em->src_y / 32,
8872 debug_src_x, debug_src_y,
8873 debug_src_x / 32, debug_src_y / 32);
8875 if (g_em->src_offset_x != 0 ||
8876 g_em->src_offset_y != 0 ||
8877 g_em->dst_offset_x != 0 ||
8878 g_em->dst_offset_y != 0)
8879 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8881 g_em->src_offset_x, g_em->src_offset_y,
8882 g_em->dst_offset_x, g_em->dst_offset_y);
8884 if (g_em->width != TILEX ||
8885 g_em->height != TILEY)
8886 printf(" %d (%d): size %d,%d should be %d,%d\n",
8888 g_em->width, g_em->height, TILEX, TILEY);
8890 num_em_gfx_errors++;
8897 for (i = 0; i < TILE_MAX; i++)
8899 for (j = 0; j < 8; j++)
8901 int element = object_mapping[i].element_rnd;
8902 int action = object_mapping[i].action;
8903 int direction = object_mapping[i].direction;
8904 boolean is_backside = object_mapping[i].is_backside;
8905 int graphic_action = el_act_dir2img(element, action, direction);
8906 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8908 if ((action == ACTION_SMASHED_BY_ROCK ||
8909 action == ACTION_SMASHED_BY_SPRING ||
8910 action == ACTION_EATING) &&
8911 graphic_action == graphic_default)
8913 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8914 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8915 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8916 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8919 // no separate animation for "smashed by rock" -- use rock instead
8920 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8921 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8923 g_em->bitmap = g_xx->bitmap;
8924 g_em->src_x = g_xx->src_x;
8925 g_em->src_y = g_xx->src_y;
8926 g_em->src_offset_x = g_xx->src_offset_x;
8927 g_em->src_offset_y = g_xx->src_offset_y;
8928 g_em->dst_offset_x = g_xx->dst_offset_x;
8929 g_em->dst_offset_y = g_xx->dst_offset_y;
8930 g_em->width = g_xx->width;
8931 g_em->height = g_xx->height;
8932 g_em->unique_identifier = g_xx->unique_identifier;
8935 g_em->preserve_background = TRUE;
8940 for (p = 0; p < MAX_PLAYERS; p++)
8942 for (i = 0; i < SPR_MAX; i++)
8944 int element = player_mapping[p][i].element_rnd;
8945 int action = player_mapping[p][i].action;
8946 int direction = player_mapping[p][i].direction;
8948 for (j = 0; j < 8; j++)
8950 int effective_element = element;
8951 int effective_action = action;
8952 int graphic = (direction == MV_NONE ?
8953 el_act2img(effective_element, effective_action) :
8954 el_act_dir2img(effective_element, effective_action,
8956 struct GraphicInfo *g = &graphic_info[graphic];
8957 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8963 Bitmap *debug_bitmap = g_em->bitmap;
8964 int debug_src_x = g_em->src_x;
8965 int debug_src_y = g_em->src_y;
8968 int frame = getAnimationFrame(g->anim_frames,
8971 g->anim_start_frame,
8974 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8976 g_em->bitmap = src_bitmap;
8977 g_em->src_x = src_x;
8978 g_em->src_y = src_y;
8979 g_em->src_offset_x = 0;
8980 g_em->src_offset_y = 0;
8981 g_em->dst_offset_x = 0;
8982 g_em->dst_offset_y = 0;
8983 g_em->width = TILEX;
8984 g_em->height = TILEY;
8988 // skip check for EMC elements not contained in original EMC artwork
8989 if (element == EL_PLAYER_3 ||
8990 element == EL_PLAYER_4)
8993 if (g_em->bitmap != debug_bitmap ||
8994 g_em->src_x != debug_src_x ||
8995 g_em->src_y != debug_src_y)
8997 static int last_i = -1;
9005 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9006 p, i, element, element_info[element].token_name,
9007 element_action_info[effective_action].suffix, direction);
9009 if (element != effective_element)
9010 printf(" [%d ('%s')]",
9012 element_info[effective_element].token_name);
9016 if (g_em->bitmap != debug_bitmap)
9017 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9018 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9020 if (g_em->src_x != debug_src_x ||
9021 g_em->src_y != debug_src_y)
9022 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9024 g_em->src_x, g_em->src_y,
9025 g_em->src_x / 32, g_em->src_y / 32,
9026 debug_src_x, debug_src_y,
9027 debug_src_x / 32, debug_src_y / 32);
9029 num_em_gfx_errors++;
9039 printf("::: [%d errors found]\n", num_em_gfx_errors);
9045 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9046 boolean any_player_moving,
9047 boolean any_player_snapping,
9048 boolean any_player_dropping)
9050 if (frame == 0 && !any_player_dropping)
9052 if (!local_player->was_waiting)
9054 if (!CheckSaveEngineSnapshotToList())
9057 local_player->was_waiting = TRUE;
9060 else if (any_player_moving || any_player_snapping || any_player_dropping)
9062 local_player->was_waiting = FALSE;
9066 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9067 boolean murphy_is_dropping)
9069 if (murphy_is_waiting)
9071 if (!local_player->was_waiting)
9073 if (!CheckSaveEngineSnapshotToList())
9076 local_player->was_waiting = TRUE;
9081 local_player->was_waiting = FALSE;
9085 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9086 boolean button_released)
9088 if (button_released)
9090 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9091 CheckSaveEngineSnapshotToList();
9093 else if (element_clicked)
9095 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9096 CheckSaveEngineSnapshotToList();
9098 game.snapshot.changed_action = TRUE;
9102 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9103 boolean any_player_moving,
9104 boolean any_player_snapping,
9105 boolean any_player_dropping)
9107 if (tape.single_step && tape.recording && !tape.pausing)
9108 if (frame == 0 && !any_player_dropping)
9109 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9111 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9112 any_player_snapping, any_player_dropping);
9115 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9116 boolean murphy_is_dropping)
9118 boolean murphy_starts_dropping = FALSE;
9121 for (i = 0; i < MAX_PLAYERS; i++)
9122 if (stored_player[i].force_dropping)
9123 murphy_starts_dropping = TRUE;
9125 if (tape.single_step && tape.recording && !tape.pausing)
9126 if (murphy_is_waiting && !murphy_starts_dropping)
9127 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9129 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9132 void CheckSingleStepMode_MM(boolean element_clicked,
9133 boolean button_released)
9135 if (tape.single_step && tape.recording && !tape.pausing)
9136 if (button_released)
9137 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9139 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9142 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9143 int graphic, int sync_frame, int x, int y)
9145 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9147 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9150 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9152 return (IS_NEXT_FRAME(sync_frame, graphic));
9155 int getGraphicInfo_Delay(int graphic)
9157 return graphic_info[graphic].anim_delay;
9160 void PlayMenuSoundExt(int sound)
9162 if (sound == SND_UNDEFINED)
9165 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9166 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9169 if (IS_LOOP_SOUND(sound))
9170 PlaySoundLoop(sound);
9175 void PlayMenuSound(void)
9177 PlayMenuSoundExt(menu.sound[game_status]);
9180 void PlayMenuSoundStereo(int sound, int stereo_position)
9182 if (sound == SND_UNDEFINED)
9185 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9186 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9189 if (IS_LOOP_SOUND(sound))
9190 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9192 PlaySoundStereo(sound, stereo_position);
9195 void PlayMenuSoundIfLoopExt(int sound)
9197 if (sound == SND_UNDEFINED)
9200 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9201 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9204 if (IS_LOOP_SOUND(sound))
9205 PlaySoundLoop(sound);
9208 void PlayMenuSoundIfLoop(void)
9210 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9213 void PlayMenuMusicExt(int music)
9215 if (music == MUS_UNDEFINED)
9218 if (!setup.sound_music)
9221 if (IS_LOOP_MUSIC(music))
9222 PlayMusicLoop(music);
9227 void PlayMenuMusic(void)
9229 char *curr_music = getCurrentlyPlayingMusicFilename();
9230 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9232 if (!strEqual(curr_music, next_music))
9233 PlayMenuMusicExt(menu.music[game_status]);
9236 void PlayMenuSoundsAndMusic(void)
9242 static void FadeMenuSounds(void)
9247 static void FadeMenuMusic(void)
9249 char *curr_music = getCurrentlyPlayingMusicFilename();
9250 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9252 if (!strEqual(curr_music, next_music))
9256 void FadeMenuSoundsAndMusic(void)
9262 void PlaySoundActivating(void)
9265 PlaySound(SND_MENU_ITEM_ACTIVATING);
9269 void PlaySoundSelecting(void)
9272 PlaySound(SND_MENU_ITEM_SELECTING);
9276 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9278 boolean change_fullscreen = (setup.fullscreen !=
9279 video.fullscreen_enabled);
9280 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9281 setup.window_scaling_percent !=
9282 video.window_scaling_percent);
9284 if (change_window_scaling_percent && video.fullscreen_enabled)
9287 if (!change_window_scaling_percent && !video.fullscreen_available)
9290 if (change_window_scaling_percent)
9292 SDLSetWindowScaling(setup.window_scaling_percent);
9296 else if (change_fullscreen)
9298 SDLSetWindowFullscreen(setup.fullscreen);
9300 // set setup value according to successfully changed fullscreen mode
9301 setup.fullscreen = video.fullscreen_enabled;
9306 if (change_fullscreen ||
9307 change_window_scaling_percent)
9309 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9311 // save backbuffer content which gets lost when toggling fullscreen mode
9312 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9314 if (change_window_scaling_percent)
9316 // keep window mode, but change window scaling
9317 video.fullscreen_enabled = TRUE; // force new window scaling
9320 // toggle fullscreen
9321 ChangeVideoModeIfNeeded(setup.fullscreen);
9323 // set setup value according to successfully changed fullscreen mode
9324 setup.fullscreen = video.fullscreen_enabled;
9326 // restore backbuffer content from temporary backbuffer backup bitmap
9327 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9329 FreeBitmap(tmp_backbuffer);
9331 // update visible window/screen
9332 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9336 static void JoinRectangles(int *x, int *y, int *width, int *height,
9337 int x2, int y2, int width2, int height2)
9339 // do not join with "off-screen" rectangle
9340 if (x2 == -1 || y2 == -1)
9345 *width = MAX(*width, width2);
9346 *height = MAX(*height, height2);
9349 void SetAnimStatus(int anim_status_new)
9351 if (anim_status_new == GAME_MODE_MAIN)
9352 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9353 else if (anim_status_new == GAME_MODE_SCORES)
9354 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9356 global.anim_status_next = anim_status_new;
9358 // directly set screen modes that are entered without fading
9359 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9360 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9361 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9362 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9363 global.anim_status = global.anim_status_next;
9366 void SetGameStatus(int game_status_new)
9368 if (game_status_new != game_status)
9369 game_status_last_screen = game_status;
9371 game_status = game_status_new;
9373 SetAnimStatus(game_status_new);
9376 void SetFontStatus(int game_status_new)
9378 static int last_game_status = -1;
9380 if (game_status_new != -1)
9382 // set game status for font use after storing last game status
9383 last_game_status = game_status;
9384 game_status = game_status_new;
9388 // reset game status after font use from last stored game status
9389 game_status = last_game_status;
9393 void ResetFontStatus(void)
9398 void SetLevelSetInfo(char *identifier, int level_nr)
9400 setString(&levelset.identifier, identifier);
9402 levelset.level_nr = level_nr;
9405 boolean CheckIfPlayfieldViewportHasChanged(void)
9407 // if game status has not changed, playfield viewport has not changed either
9408 if (game_status == game_status_last)
9411 // check if playfield viewport has changed with current game status
9412 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9413 int new_real_sx = vp_playfield->x;
9414 int new_real_sy = vp_playfield->y;
9415 int new_full_sxsize = vp_playfield->width;
9416 int new_full_sysize = vp_playfield->height;
9418 return (new_real_sx != REAL_SX ||
9419 new_real_sy != REAL_SY ||
9420 new_full_sxsize != FULL_SXSIZE ||
9421 new_full_sysize != FULL_SYSIZE);
9424 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged(void)
9426 return (CheckIfGlobalBorderHasChanged() ||
9427 CheckIfPlayfieldViewportHasChanged());
9430 void ChangeViewportPropertiesIfNeeded(void)
9432 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9433 FALSE : setup.small_game_graphics);
9434 int gfx_game_mode = game_status;
9435 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9437 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9438 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9439 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9440 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9441 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9442 int new_win_xsize = vp_window->width;
9443 int new_win_ysize = vp_window->height;
9444 int border_size = vp_playfield->border_size;
9445 int new_sx = vp_playfield->x + border_size;
9446 int new_sy = vp_playfield->y + border_size;
9447 int new_sxsize = vp_playfield->width - 2 * border_size;
9448 int new_sysize = vp_playfield->height - 2 * border_size;
9449 int new_real_sx = vp_playfield->x;
9450 int new_real_sy = vp_playfield->y;
9451 int new_full_sxsize = vp_playfield->width;
9452 int new_full_sysize = vp_playfield->height;
9453 int new_dx = vp_door_1->x;
9454 int new_dy = vp_door_1->y;
9455 int new_dxsize = vp_door_1->width;
9456 int new_dysize = vp_door_1->height;
9457 int new_vx = vp_door_2->x;
9458 int new_vy = vp_door_2->y;
9459 int new_vxsize = vp_door_2->width;
9460 int new_vysize = vp_door_2->height;
9461 int new_ex = vp_door_3->x;
9462 int new_ey = vp_door_3->y;
9463 int new_exsize = vp_door_3->width;
9464 int new_eysize = vp_door_3->height;
9465 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9466 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9467 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9468 int new_scr_fieldx = new_sxsize / tilesize;
9469 int new_scr_fieldy = new_sysize / tilesize;
9470 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9471 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9472 boolean init_gfx_buffers = FALSE;
9473 boolean init_video_buffer = FALSE;
9474 boolean init_gadgets_and_anims = FALSE;
9475 boolean init_em_graphics = FALSE;
9477 if (new_win_xsize != WIN_XSIZE ||
9478 new_win_ysize != WIN_YSIZE)
9480 WIN_XSIZE = new_win_xsize;
9481 WIN_YSIZE = new_win_ysize;
9483 init_video_buffer = TRUE;
9484 init_gfx_buffers = TRUE;
9485 init_gadgets_and_anims = TRUE;
9487 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9490 if (new_scr_fieldx != SCR_FIELDX ||
9491 new_scr_fieldy != SCR_FIELDY)
9493 // this always toggles between MAIN and GAME when using small tile size
9495 SCR_FIELDX = new_scr_fieldx;
9496 SCR_FIELDY = new_scr_fieldy;
9498 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9509 new_sxsize != SXSIZE ||
9510 new_sysize != SYSIZE ||
9511 new_dxsize != DXSIZE ||
9512 new_dysize != DYSIZE ||
9513 new_vxsize != VXSIZE ||
9514 new_vysize != VYSIZE ||
9515 new_exsize != EXSIZE ||
9516 new_eysize != EYSIZE ||
9517 new_real_sx != REAL_SX ||
9518 new_real_sy != REAL_SY ||
9519 new_full_sxsize != FULL_SXSIZE ||
9520 new_full_sysize != FULL_SYSIZE ||
9521 new_tilesize_var != TILESIZE_VAR
9524 // ------------------------------------------------------------------------
9525 // determine next fading area for changed viewport definitions
9526 // ------------------------------------------------------------------------
9528 // start with current playfield area (default fading area)
9531 FADE_SXSIZE = FULL_SXSIZE;
9532 FADE_SYSIZE = FULL_SYSIZE;
9534 // add new playfield area if position or size has changed
9535 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9536 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9538 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9539 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9542 // add current and new door 1 area if position or size has changed
9543 if (new_dx != DX || new_dy != DY ||
9544 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9546 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9547 DX, DY, DXSIZE, DYSIZE);
9548 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9549 new_dx, new_dy, new_dxsize, new_dysize);
9552 // add current and new door 2 area if position or size has changed
9553 if (new_dx != VX || new_dy != VY ||
9554 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9556 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9557 VX, VY, VXSIZE, VYSIZE);
9558 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9559 new_vx, new_vy, new_vxsize, new_vysize);
9562 // ------------------------------------------------------------------------
9563 // handle changed tile size
9564 // ------------------------------------------------------------------------
9566 if (new_tilesize_var != TILESIZE_VAR)
9568 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9570 // changing tile size invalidates scroll values of engine snapshots
9571 FreeEngineSnapshotSingle();
9573 // changing tile size requires update of graphic mapping for EM engine
9574 init_em_graphics = TRUE;
9585 SXSIZE = new_sxsize;
9586 SYSIZE = new_sysize;
9587 DXSIZE = new_dxsize;
9588 DYSIZE = new_dysize;
9589 VXSIZE = new_vxsize;
9590 VYSIZE = new_vysize;
9591 EXSIZE = new_exsize;
9592 EYSIZE = new_eysize;
9593 REAL_SX = new_real_sx;
9594 REAL_SY = new_real_sy;
9595 FULL_SXSIZE = new_full_sxsize;
9596 FULL_SYSIZE = new_full_sysize;
9597 TILESIZE_VAR = new_tilesize_var;
9599 init_gfx_buffers = TRUE;
9600 init_gadgets_and_anims = TRUE;
9602 // printf("::: viewports: init_gfx_buffers\n");
9603 // printf("::: viewports: init_gadgets_and_anims\n");
9606 if (init_gfx_buffers)
9608 // printf("::: init_gfx_buffers\n");
9610 SCR_FIELDX = new_scr_fieldx_buffers;
9611 SCR_FIELDY = new_scr_fieldy_buffers;
9615 SCR_FIELDX = new_scr_fieldx;
9616 SCR_FIELDY = new_scr_fieldy;
9618 SetDrawDeactivationMask(REDRAW_NONE);
9619 SetDrawBackgroundMask(REDRAW_FIELD);
9622 if (init_video_buffer)
9624 // printf("::: init_video_buffer\n");
9626 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9627 InitImageTextures();
9630 if (init_gadgets_and_anims)
9632 // printf("::: init_gadgets_and_anims\n");
9635 InitGlobalAnimations();
9638 if (init_em_graphics)
9640 InitGraphicInfo_EM();