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();
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()
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()
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()
484 if (game.envelope_active)
487 DrawLevel(REDRAW_ALL);
491 void RedrawPlayfield()
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()
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 void DrawFramesPerSecond()
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()
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);
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 */
958 else /* REDRAW_ALL */
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;
986 static void SetScreenStates_BeforeFadingIn()
988 // temporarily set screen mode for animations to screen after fading in
989 global.anim_status = global.anim_status_next;
991 // store backbuffer with all animations that will be started after fading in
992 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
993 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
995 // set screen mode for animations back to fading
996 global.anim_status = GAME_MODE_PSEUDO_FADING;
999 static void SetScreenStates_AfterFadingIn()
1001 // store new source screen (to use correct masked border for fading)
1002 gfx.fade_border_source_status = global.border_status;
1004 global.anim_status = global.anim_status_next;
1007 static void SetScreenStates_BeforeFadingOut()
1009 // store new target screen (to use correct masked border for fading)
1010 gfx.fade_border_target_status = game_status;
1012 // set screen mode for animations to fading
1013 global.anim_status = GAME_MODE_PSEUDO_FADING;
1015 // store backbuffer with all animations that will be stopped for fading out
1016 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1017 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1020 static void SetScreenStates_AfterFadingOut()
1022 global.border_status = game_status;
1025 void FadeIn(int fade_mask)
1027 SetScreenStates_BeforeFadingIn();
1030 DrawMaskedBorder(REDRAW_ALL);
1033 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1034 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1036 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1040 FADE_SXSIZE = FULL_SXSIZE;
1041 FADE_SYSIZE = FULL_SYSIZE;
1043 if (game_status == GAME_MODE_PLAYING &&
1044 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1045 SetOverlayActive(TRUE);
1047 SetScreenStates_AfterFadingIn();
1049 // force update of global animation status in case of rapid screen changes
1050 redraw_mask = REDRAW_ALL;
1054 void FadeOut(int fade_mask)
1056 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1057 if (!equalRedrawMasks(fade_mask, redraw_mask))
1060 SetScreenStates_BeforeFadingOut();
1062 SetTileCursorActive(FALSE);
1063 SetOverlayActive(FALSE);
1066 DrawMaskedBorder(REDRAW_ALL);
1069 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1070 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1072 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1074 SetScreenStates_AfterFadingOut();
1077 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1079 static struct TitleFadingInfo fading_leave_stored;
1082 fading_leave_stored = fading_leave;
1084 fading = fading_leave_stored;
1087 void FadeSetEnterMenu()
1089 fading = menu.enter_menu;
1091 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1094 void FadeSetLeaveMenu()
1096 fading = menu.leave_menu;
1098 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1101 void FadeSetEnterScreen()
1103 fading = menu.enter_screen[game_status];
1105 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1108 void FadeSetNextScreen()
1110 fading = menu.next_screen[game_status];
1112 // (do not overwrite fade mode set by FadeSetEnterScreen)
1113 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1116 void FadeSetLeaveScreen()
1118 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1121 void FadeSetFromType(int type)
1123 if (type & TYPE_ENTER_SCREEN)
1124 FadeSetEnterScreen();
1125 else if (type & TYPE_ENTER)
1127 else if (type & TYPE_LEAVE)
1131 void FadeSetDisabled()
1133 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1135 fading = fading_none;
1138 void FadeSkipNextFadeIn()
1140 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1143 void FadeSkipNextFadeOut()
1145 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1148 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1150 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1152 return (graphic == IMG_UNDEFINED ? NULL :
1153 graphic_info[graphic].bitmap != NULL || redefined ?
1154 graphic_info[graphic].bitmap :
1155 graphic_info[default_graphic].bitmap);
1158 Bitmap *getBackgroundBitmap(int graphic)
1160 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1163 Bitmap *getGlobalBorderBitmap(int graphic)
1165 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1168 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1171 (status == GAME_MODE_MAIN ||
1172 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1173 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1174 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1175 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1178 return getGlobalBorderBitmap(graphic);
1181 void SetWindowBackgroundImageIfDefined(int graphic)
1183 if (graphic_info[graphic].bitmap)
1184 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1187 void SetMainBackgroundImageIfDefined(int graphic)
1189 if (graphic_info[graphic].bitmap)
1190 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1193 void SetDoorBackgroundImageIfDefined(int graphic)
1195 if (graphic_info[graphic].bitmap)
1196 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1199 void SetWindowBackgroundImage(int graphic)
1201 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1204 void SetMainBackgroundImage(int graphic)
1206 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1209 void SetDoorBackgroundImage(int graphic)
1211 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1214 void SetPanelBackground()
1216 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1218 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1219 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1221 SetDoorBackgroundBitmap(bitmap_db_panel);
1224 void DrawBackground(int x, int y, int width, int height)
1226 /* "drawto" might still point to playfield buffer here (hall of fame) */
1227 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1229 if (IN_GFX_FIELD_FULL(x, y))
1230 redraw_mask |= REDRAW_FIELD;
1231 else if (IN_GFX_DOOR_1(x, y))
1232 redraw_mask |= REDRAW_DOOR_1;
1233 else if (IN_GFX_DOOR_2(x, y))
1234 redraw_mask |= REDRAW_DOOR_2;
1235 else if (IN_GFX_DOOR_3(x, y))
1236 redraw_mask |= REDRAW_DOOR_3;
1239 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1241 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1243 if (font->bitmap == NULL)
1246 DrawBackground(x, y, width, height);
1249 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1251 struct GraphicInfo *g = &graphic_info[graphic];
1253 if (g->bitmap == NULL)
1256 DrawBackground(x, y, width, height);
1259 static int game_status_last = -1;
1260 static Bitmap *global_border_bitmap_last = NULL;
1261 static Bitmap *global_border_bitmap = NULL;
1262 static int real_sx_last = -1, real_sy_last = -1;
1263 static int full_sxsize_last = -1, full_sysize_last = -1;
1264 static int dx_last = -1, dy_last = -1;
1265 static int dxsize_last = -1, dysize_last = -1;
1266 static int vx_last = -1, vy_last = -1;
1267 static int vxsize_last = -1, vysize_last = -1;
1269 boolean CheckIfGlobalBorderHasChanged()
1271 // if game status has not changed, global border has not changed either
1272 if (game_status == game_status_last)
1275 // determine and store new global border bitmap for current game status
1276 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1278 return (global_border_bitmap_last != global_border_bitmap);
1281 boolean CheckIfGlobalBorderRedrawIsNeeded()
1283 // if game status has not changed, nothing has to be redrawn
1284 if (game_status == game_status_last)
1287 // redraw if last screen was title screen
1288 if (game_status_last == GAME_MODE_TITLE)
1291 // redraw if global screen border has changed
1292 if (CheckIfGlobalBorderHasChanged())
1295 // redraw if position or size of playfield area has changed
1296 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1297 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1300 // redraw if position or size of door area has changed
1301 if (dx_last != DX || dy_last != DY ||
1302 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1305 // redraw if position or size of tape area has changed
1306 if (vx_last != VX || vy_last != VY ||
1307 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1313 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1316 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1318 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1321 void RedrawGlobalBorder()
1323 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1325 RedrawGlobalBorderFromBitmap(bitmap);
1327 redraw_mask = REDRAW_ALL;
1330 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1332 static void RedrawGlobalBorderIfNeeded()
1334 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1335 if (game_status == game_status_last)
1339 // copy current draw buffer to later copy back areas that have not changed
1340 if (game_status_last != GAME_MODE_TITLE)
1341 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1343 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1344 if (CheckIfGlobalBorderRedrawIsNeeded())
1347 // redraw global screen border (or clear, if defined to be empty)
1348 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1350 // copy previous playfield and door areas, if they are defined on both
1351 // previous and current screen and if they still have the same size
1353 if (real_sx_last != -1 && real_sy_last != -1 &&
1354 REAL_SX != -1 && REAL_SY != -1 &&
1355 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1356 BlitBitmap(bitmap_db_store_1, backbuffer,
1357 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1360 if (dx_last != -1 && dy_last != -1 &&
1361 DX != -1 && DY != -1 &&
1362 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1363 BlitBitmap(bitmap_db_store_1, backbuffer,
1364 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1366 if (vx_last != -1 && vy_last != -1 &&
1367 VX != -1 && VY != -1 &&
1368 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1369 BlitBitmap(bitmap_db_store_1, backbuffer,
1370 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1372 redraw_mask = REDRAW_ALL;
1375 game_status_last = game_status;
1377 global_border_bitmap_last = global_border_bitmap;
1379 real_sx_last = REAL_SX;
1380 real_sy_last = REAL_SY;
1381 full_sxsize_last = FULL_SXSIZE;
1382 full_sysize_last = FULL_SYSIZE;
1385 dxsize_last = DXSIZE;
1386 dysize_last = DYSIZE;
1389 vxsize_last = VXSIZE;
1390 vysize_last = VYSIZE;
1395 RedrawGlobalBorderIfNeeded();
1397 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1398 /* (when entering hall of fame after playing) */
1399 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1401 /* !!! maybe this should be done before clearing the background !!! */
1402 if (game_status == GAME_MODE_PLAYING)
1404 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1405 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1409 SetDrawtoField(DRAW_TO_BACKBUFFER);
1413 void MarkTileDirty(int x, int y)
1415 redraw_mask |= REDRAW_FIELD;
1418 void SetBorderElement()
1422 BorderElement = EL_EMPTY;
1424 /* the MM game engine does not use a visible border element */
1425 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1428 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1430 for (x = 0; x < lev_fieldx; x++)
1432 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1433 BorderElement = EL_STEELWALL;
1435 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1441 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1442 int max_array_fieldx, int max_array_fieldy,
1443 short field[max_array_fieldx][max_array_fieldy],
1444 int max_fieldx, int max_fieldy)
1448 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1449 static int safety = 0;
1451 /* check if starting field still has the desired content */
1452 if (field[from_x][from_y] == fill_element)
1457 if (safety > max_fieldx * max_fieldy)
1458 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1460 old_element = field[from_x][from_y];
1461 field[from_x][from_y] = fill_element;
1463 for (i = 0; i < 4; i++)
1465 x = from_x + check[i][0];
1466 y = from_y + check[i][1];
1468 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1469 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1470 field, max_fieldx, max_fieldy);
1476 void FloodFillLevel(int from_x, int from_y, int fill_element,
1477 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1478 int max_fieldx, int max_fieldy)
1480 FloodFillLevelExt(from_x, from_y, fill_element,
1481 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1482 max_fieldx, max_fieldy);
1485 void SetRandomAnimationValue(int x, int y)
1487 gfx.anim_random_frame = GfxRandom[x][y];
1490 int getGraphicAnimationFrame(int graphic, int sync_frame)
1492 /* animation synchronized with global frame counter, not move position */
1493 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1494 sync_frame = FrameCounter;
1496 return getAnimationFrame(graphic_info[graphic].anim_frames,
1497 graphic_info[graphic].anim_delay,
1498 graphic_info[graphic].anim_mode,
1499 graphic_info[graphic].anim_start_frame,
1503 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1505 struct GraphicInfo *g = &graphic_info[graphic];
1506 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1508 if (tilesize == gfx.standard_tile_size)
1509 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1510 else if (tilesize == game.tile_size)
1511 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1513 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1516 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1517 boolean get_backside)
1519 struct GraphicInfo *g = &graphic_info[graphic];
1520 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1521 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1523 if (g->offset_y == 0) /* frames are ordered horizontally */
1525 int max_width = g->anim_frames_per_line * g->width;
1526 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1528 *x = pos % max_width;
1529 *y = src_y % g->height + pos / max_width * g->height;
1531 else if (g->offset_x == 0) /* frames are ordered vertically */
1533 int max_height = g->anim_frames_per_line * g->height;
1534 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1536 *x = src_x % g->width + pos / max_height * g->width;
1537 *y = pos % max_height;
1539 else /* frames are ordered diagonally */
1541 *x = src_x + frame * g->offset_x;
1542 *y = src_y + frame * g->offset_y;
1546 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1547 Bitmap **bitmap, int *x, int *y,
1548 boolean get_backside)
1550 struct GraphicInfo *g = &graphic_info[graphic];
1552 // if no in-game graphics defined, always use standard graphic size
1553 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1554 tilesize = TILESIZE;
1556 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1557 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1559 *x = *x * tilesize / g->tile_size;
1560 *y = *y * tilesize / g->tile_size;
1563 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1564 Bitmap **bitmap, int *x, int *y)
1566 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1569 void getFixedGraphicSource(int graphic, int frame,
1570 Bitmap **bitmap, int *x, int *y)
1572 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1575 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1577 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1580 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1581 int *x, int *y, boolean get_backside)
1583 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1587 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1589 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1592 void DrawGraphic(int x, int y, int graphic, int frame)
1595 if (!IN_SCR_FIELD(x, y))
1597 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1598 printf("DrawGraphic(): This should never happen!\n");
1603 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1606 MarkTileDirty(x, y);
1609 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1612 if (!IN_SCR_FIELD(x, y))
1614 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1615 printf("DrawGraphic(): This should never happen!\n");
1620 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1622 MarkTileDirty(x, y);
1625 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1631 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1633 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1636 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1642 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1643 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1646 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1649 if (!IN_SCR_FIELD(x, y))
1651 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1652 printf("DrawGraphicThruMask(): This should never happen!\n");
1657 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1660 MarkTileDirty(x, y);
1663 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1666 if (!IN_SCR_FIELD(x, y))
1668 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1669 printf("DrawGraphicThruMask(): This should never happen!\n");
1674 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1676 MarkTileDirty(x, y);
1679 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1685 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1687 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1691 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1692 int graphic, int frame)
1697 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1699 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1703 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1705 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1707 MarkTileDirty(x / tilesize, y / tilesize);
1710 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1713 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1714 graphic, frame, tilesize);
1715 MarkTileDirty(x / tilesize, y / tilesize);
1718 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1724 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1725 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1728 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1729 int frame, int tilesize)
1734 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1735 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1738 void DrawMiniGraphic(int x, int y, int graphic)
1740 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1741 MarkTileDirty(x / 2, y / 2);
1744 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1749 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1750 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1753 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1754 int graphic, int frame,
1755 int cut_mode, int mask_mode)
1760 int width = TILEX, height = TILEY;
1763 if (dx || dy) /* shifted graphic */
1765 if (x < BX1) /* object enters playfield from the left */
1772 else if (x > BX2) /* object enters playfield from the right */
1778 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1784 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1786 else if (dx) /* general horizontal movement */
1787 MarkTileDirty(x + SIGN(dx), y);
1789 if (y < BY1) /* object enters playfield from the top */
1791 if (cut_mode == CUT_BELOW) /* object completely above top border */
1799 else if (y > BY2) /* object enters playfield from the bottom */
1805 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1811 else if (dy > 0 && cut_mode == CUT_ABOVE)
1813 if (y == BY2) /* object completely above bottom border */
1819 MarkTileDirty(x, y + 1);
1820 } /* object leaves playfield to the bottom */
1821 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1823 else if (dy) /* general vertical movement */
1824 MarkTileDirty(x, y + SIGN(dy));
1828 if (!IN_SCR_FIELD(x, y))
1830 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1831 printf("DrawGraphicShifted(): This should never happen!\n");
1836 width = width * TILESIZE_VAR / TILESIZE;
1837 height = height * TILESIZE_VAR / TILESIZE;
1838 cx = cx * TILESIZE_VAR / TILESIZE;
1839 cy = cy * TILESIZE_VAR / TILESIZE;
1840 dx = dx * TILESIZE_VAR / TILESIZE;
1841 dy = dy * TILESIZE_VAR / TILESIZE;
1843 if (width > 0 && height > 0)
1845 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1850 dst_x = FX + x * TILEX_VAR + dx;
1851 dst_y = FY + y * TILEY_VAR + dy;
1853 if (mask_mode == USE_MASKING)
1854 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1857 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1860 MarkTileDirty(x, y);
1864 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1865 int graphic, int frame,
1866 int cut_mode, int mask_mode)
1871 int width = TILEX_VAR, height = TILEY_VAR;
1874 int x2 = x + SIGN(dx);
1875 int y2 = y + SIGN(dy);
1877 /* movement with two-tile animations must be sync'ed with movement position,
1878 not with current GfxFrame (which can be higher when using slow movement) */
1879 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1880 int anim_frames = graphic_info[graphic].anim_frames;
1882 /* (we also need anim_delay here for movement animations with less frames) */
1883 int anim_delay = graphic_info[graphic].anim_delay;
1884 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1886 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1887 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1889 /* re-calculate animation frame for two-tile movement animation */
1890 frame = getGraphicAnimationFrame(graphic, sync_frame);
1892 /* check if movement start graphic inside screen area and should be drawn */
1893 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1895 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1897 dst_x = FX + x1 * TILEX_VAR;
1898 dst_y = FY + y1 * TILEY_VAR;
1900 if (mask_mode == USE_MASKING)
1901 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1904 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1907 MarkTileDirty(x1, y1);
1910 /* check if movement end graphic inside screen area and should be drawn */
1911 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1913 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1915 dst_x = FX + x2 * TILEX_VAR;
1916 dst_y = FY + y2 * TILEY_VAR;
1918 if (mask_mode == USE_MASKING)
1919 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1922 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1925 MarkTileDirty(x2, y2);
1929 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1930 int graphic, int frame,
1931 int cut_mode, int mask_mode)
1935 DrawGraphic(x, y, graphic, frame);
1940 if (graphic_info[graphic].double_movement) /* EM style movement images */
1941 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1943 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1946 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1947 int frame, int cut_mode)
1949 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1952 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1953 int cut_mode, int mask_mode)
1955 int lx = LEVELX(x), ly = LEVELY(y);
1959 if (IN_LEV_FIELD(lx, ly))
1961 SetRandomAnimationValue(lx, ly);
1963 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1964 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1966 /* do not use double (EM style) movement graphic when not moving */
1967 if (graphic_info[graphic].double_movement && !dx && !dy)
1969 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1970 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1973 else /* border element */
1975 graphic = el2img(element);
1976 frame = getGraphicAnimationFrame(graphic, -1);
1979 if (element == EL_EXPANDABLE_WALL)
1981 boolean left_stopped = FALSE, right_stopped = FALSE;
1983 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1984 left_stopped = TRUE;
1985 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1986 right_stopped = TRUE;
1988 if (left_stopped && right_stopped)
1990 else if (left_stopped)
1992 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1993 frame = graphic_info[graphic].anim_frames - 1;
1995 else if (right_stopped)
1997 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1998 frame = graphic_info[graphic].anim_frames - 1;
2003 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2004 else if (mask_mode == USE_MASKING)
2005 DrawGraphicThruMask(x, y, graphic, frame);
2007 DrawGraphic(x, y, graphic, frame);
2010 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2011 int cut_mode, int mask_mode)
2013 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2014 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2015 cut_mode, mask_mode);
2018 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2021 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2024 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2027 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2030 void DrawLevelElementThruMask(int x, int y, int element)
2032 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2035 void DrawLevelFieldThruMask(int x, int y)
2037 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2040 /* !!! implementation of quicksand is totally broken !!! */
2041 #define IS_CRUMBLED_TILE(x, y, e) \
2042 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2043 !IS_MOVING(x, y) || \
2044 (e) == EL_QUICKSAND_EMPTYING || \
2045 (e) == EL_QUICKSAND_FAST_EMPTYING))
2047 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2052 int width, height, cx, cy;
2053 int sx = SCREENX(x), sy = SCREENY(y);
2054 int crumbled_border_size = graphic_info[graphic].border_size;
2055 int crumbled_tile_size = graphic_info[graphic].tile_size;
2056 int crumbled_border_size_var =
2057 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2060 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2062 for (i = 1; i < 4; i++)
2064 int dxx = (i & 1 ? dx : 0);
2065 int dyy = (i & 2 ? dy : 0);
2068 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2071 /* check if neighbour field is of same crumble type */
2072 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2073 graphic_info[graphic].class ==
2074 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2076 /* return if check prevents inner corner */
2077 if (same == (dxx == dx && dyy == dy))
2081 /* if we reach this point, we have an inner corner */
2083 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2085 width = crumbled_border_size_var;
2086 height = crumbled_border_size_var;
2087 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2088 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2090 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2091 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2094 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2099 int width, height, bx, by, cx, cy;
2100 int sx = SCREENX(x), sy = SCREENY(y);
2101 int crumbled_border_size = graphic_info[graphic].border_size;
2102 int crumbled_tile_size = graphic_info[graphic].tile_size;
2103 int crumbled_border_size_var =
2104 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2105 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2108 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2110 /* draw simple, sloppy, non-corner-accurate crumbled border */
2112 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2113 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2114 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2115 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2117 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2118 FX + sx * TILEX_VAR + cx,
2119 FY + sy * TILEY_VAR + cy);
2121 /* (remaining middle border part must be at least as big as corner part) */
2122 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2123 crumbled_border_size_var >= TILESIZE_VAR / 3)
2126 /* correct corners of crumbled border, if needed */
2128 for (i = -1; i <= 1; i += 2)
2130 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2131 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2132 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2135 /* check if neighbour field is of same crumble type */
2136 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2137 graphic_info[graphic].class ==
2138 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2140 /* no crumbled corner, but continued crumbled border */
2142 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2143 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2144 int b1 = (i == 1 ? crumbled_border_size_var :
2145 TILESIZE_VAR - 2 * crumbled_border_size_var);
2147 width = crumbled_border_size_var;
2148 height = crumbled_border_size_var;
2150 if (dir == 1 || dir == 2)
2165 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2167 FX + sx * TILEX_VAR + cx,
2168 FY + sy * TILEY_VAR + cy);
2173 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2175 int sx = SCREENX(x), sy = SCREENY(y);
2178 static int xy[4][2] =
2186 if (!IN_LEV_FIELD(x, y))
2189 element = TILE_GFX_ELEMENT(x, y);
2191 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2193 if (!IN_SCR_FIELD(sx, sy))
2196 /* crumble field borders towards direct neighbour fields */
2197 for (i = 0; i < 4; i++)
2199 int xx = x + xy[i][0];
2200 int yy = y + xy[i][1];
2202 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2205 /* check if neighbour field is of same crumble type */
2206 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2207 graphic_info[graphic].class ==
2208 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2211 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2214 /* crumble inner field corners towards corner neighbour fields */
2215 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2216 graphic_info[graphic].anim_frames == 2)
2218 for (i = 0; i < 4; i++)
2220 int dx = (i & 1 ? +1 : -1);
2221 int dy = (i & 2 ? +1 : -1);
2223 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2227 MarkTileDirty(sx, sy);
2229 else /* center field is not crumbled -- crumble neighbour fields */
2231 /* crumble field borders of direct neighbour fields */
2232 for (i = 0; i < 4; i++)
2234 int xx = x + xy[i][0];
2235 int yy = y + xy[i][1];
2236 int sxx = sx + xy[i][0];
2237 int syy = sy + xy[i][1];
2239 if (!IN_LEV_FIELD(xx, yy) ||
2240 !IN_SCR_FIELD(sxx, syy))
2243 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2246 element = TILE_GFX_ELEMENT(xx, yy);
2248 if (!IS_CRUMBLED_TILE(xx, yy, element))
2251 graphic = el_act2crm(element, ACTION_DEFAULT);
2253 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2255 MarkTileDirty(sxx, syy);
2258 /* crumble inner field corners of corner neighbour fields */
2259 for (i = 0; i < 4; i++)
2261 int dx = (i & 1 ? +1 : -1);
2262 int dy = (i & 2 ? +1 : -1);
2268 if (!IN_LEV_FIELD(xx, yy) ||
2269 !IN_SCR_FIELD(sxx, syy))
2272 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2275 element = TILE_GFX_ELEMENT(xx, yy);
2277 if (!IS_CRUMBLED_TILE(xx, yy, element))
2280 graphic = el_act2crm(element, ACTION_DEFAULT);
2282 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2283 graphic_info[graphic].anim_frames == 2)
2284 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2286 MarkTileDirty(sxx, syy);
2291 void DrawLevelFieldCrumbled(int x, int y)
2295 if (!IN_LEV_FIELD(x, y))
2298 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2299 GfxElement[x][y] != EL_UNDEFINED &&
2300 GFX_CRUMBLED(GfxElement[x][y]))
2302 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2307 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2309 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2312 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2315 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2316 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2317 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2318 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2319 int sx = SCREENX(x), sy = SCREENY(y);
2321 DrawGraphic(sx, sy, graphic1, frame1);
2322 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2325 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2327 int sx = SCREENX(x), sy = SCREENY(y);
2328 static int xy[4][2] =
2337 /* crumble direct neighbour fields (required for field borders) */
2338 for (i = 0; i < 4; i++)
2340 int xx = x + xy[i][0];
2341 int yy = y + xy[i][1];
2342 int sxx = sx + xy[i][0];
2343 int syy = sy + xy[i][1];
2345 if (!IN_LEV_FIELD(xx, yy) ||
2346 !IN_SCR_FIELD(sxx, syy) ||
2347 !GFX_CRUMBLED(Feld[xx][yy]) ||
2351 DrawLevelField(xx, yy);
2354 /* crumble corner neighbour fields (required for inner field corners) */
2355 for (i = 0; i < 4; i++)
2357 int dx = (i & 1 ? +1 : -1);
2358 int dy = (i & 2 ? +1 : -1);
2364 if (!IN_LEV_FIELD(xx, yy) ||
2365 !IN_SCR_FIELD(sxx, syy) ||
2366 !GFX_CRUMBLED(Feld[xx][yy]) ||
2370 int element = TILE_GFX_ELEMENT(xx, yy);
2371 int graphic = el_act2crm(element, ACTION_DEFAULT);
2373 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2374 graphic_info[graphic].anim_frames == 2)
2375 DrawLevelField(xx, yy);
2379 static int getBorderElement(int x, int y)
2383 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2384 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2385 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2386 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2387 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2388 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2389 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2391 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2392 int steel_position = (x == -1 && y == -1 ? 0 :
2393 x == lev_fieldx && y == -1 ? 1 :
2394 x == -1 && y == lev_fieldy ? 2 :
2395 x == lev_fieldx && y == lev_fieldy ? 3 :
2396 x == -1 || x == lev_fieldx ? 4 :
2397 y == -1 || y == lev_fieldy ? 5 : 6);
2399 return border[steel_position][steel_type];
2402 void DrawScreenElement(int x, int y, int element)
2404 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2405 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2408 void DrawLevelElement(int x, int y, int element)
2410 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2411 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2414 void DrawScreenField(int x, int y)
2416 int lx = LEVELX(x), ly = LEVELY(y);
2417 int element, content;
2419 if (!IN_LEV_FIELD(lx, ly))
2421 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2424 element = getBorderElement(lx, ly);
2426 DrawScreenElement(x, y, element);
2431 element = Feld[lx][ly];
2432 content = Store[lx][ly];
2434 if (IS_MOVING(lx, ly))
2436 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2437 boolean cut_mode = NO_CUTTING;
2439 if (element == EL_QUICKSAND_EMPTYING ||
2440 element == EL_QUICKSAND_FAST_EMPTYING ||
2441 element == EL_MAGIC_WALL_EMPTYING ||
2442 element == EL_BD_MAGIC_WALL_EMPTYING ||
2443 element == EL_DC_MAGIC_WALL_EMPTYING ||
2444 element == EL_AMOEBA_DROPPING)
2445 cut_mode = CUT_ABOVE;
2446 else if (element == EL_QUICKSAND_FILLING ||
2447 element == EL_QUICKSAND_FAST_FILLING ||
2448 element == EL_MAGIC_WALL_FILLING ||
2449 element == EL_BD_MAGIC_WALL_FILLING ||
2450 element == EL_DC_MAGIC_WALL_FILLING)
2451 cut_mode = CUT_BELOW;
2453 if (cut_mode == CUT_ABOVE)
2454 DrawScreenElement(x, y, element);
2456 DrawScreenElement(x, y, EL_EMPTY);
2459 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2460 else if (cut_mode == NO_CUTTING)
2461 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2464 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2466 if (cut_mode == CUT_BELOW &&
2467 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2468 DrawLevelElement(lx, ly + 1, element);
2471 if (content == EL_ACID)
2473 int dir = MovDir[lx][ly];
2474 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2475 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2477 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2479 // prevent target field from being drawn again (but without masking)
2480 // (this would happen if target field is scanned after moving element)
2481 Stop[newlx][newly] = TRUE;
2484 else if (IS_BLOCKED(lx, ly))
2489 boolean cut_mode = NO_CUTTING;
2490 int element_old, content_old;
2492 Blocked2Moving(lx, ly, &oldx, &oldy);
2495 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2496 MovDir[oldx][oldy] == MV_RIGHT);
2498 element_old = Feld[oldx][oldy];
2499 content_old = Store[oldx][oldy];
2501 if (element_old == EL_QUICKSAND_EMPTYING ||
2502 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2503 element_old == EL_MAGIC_WALL_EMPTYING ||
2504 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2505 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2506 element_old == EL_AMOEBA_DROPPING)
2507 cut_mode = CUT_ABOVE;
2509 DrawScreenElement(x, y, EL_EMPTY);
2512 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2514 else if (cut_mode == NO_CUTTING)
2515 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2518 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2521 else if (IS_DRAWABLE(element))
2522 DrawScreenElement(x, y, element);
2524 DrawScreenElement(x, y, EL_EMPTY);
2527 void DrawLevelField(int x, int y)
2529 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2530 DrawScreenField(SCREENX(x), SCREENY(y));
2531 else if (IS_MOVING(x, y))
2535 Moving2Blocked(x, y, &newx, &newy);
2536 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2537 DrawScreenField(SCREENX(newx), SCREENY(newy));
2539 else if (IS_BLOCKED(x, y))
2543 Blocked2Moving(x, y, &oldx, &oldy);
2544 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2545 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2549 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2550 int (*el2img_function)(int), boolean masked,
2551 int element_bits_draw)
2553 int element_base = map_mm_wall_element(element);
2554 int element_bits = (IS_DF_WALL(element) ?
2555 element - EL_DF_WALL_START :
2556 IS_MM_WALL(element) ?
2557 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2558 int graphic = el2img_function(element_base);
2559 int tilesize_draw = tilesize / 2;
2564 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2566 for (i = 0; i < 4; i++)
2568 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2569 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2571 if (!(element_bits_draw & (1 << i)))
2574 if (element_bits & (1 << i))
2577 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2578 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2580 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2581 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2586 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2587 tilesize_draw, tilesize_draw);
2592 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2593 boolean masked, int element_bits_draw)
2595 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2596 element, tilesize, el2edimg, masked, element_bits_draw);
2599 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2600 int (*el2img_function)(int))
2602 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2606 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2609 if (IS_MM_WALL(element))
2611 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2612 element, tilesize, el2edimg, masked, 0x000f);
2616 int graphic = el2edimg(element);
2619 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2621 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2625 void DrawSizedElement(int x, int y, int element, int tilesize)
2627 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2630 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2632 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2635 void DrawMiniElement(int x, int y, int element)
2639 graphic = el2edimg(element);
2640 DrawMiniGraphic(x, y, graphic);
2643 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2646 int x = sx + scroll_x, y = sy + scroll_y;
2648 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2649 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2650 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2651 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2653 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2656 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2658 int x = sx + scroll_x, y = sy + scroll_y;
2660 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2661 DrawMiniElement(sx, sy, EL_EMPTY);
2662 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2663 DrawMiniElement(sx, sy, Feld[x][y]);
2665 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2668 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2669 int x, int y, int xsize, int ysize,
2670 int tile_width, int tile_height)
2674 int dst_x = startx + x * tile_width;
2675 int dst_y = starty + y * tile_height;
2676 int width = graphic_info[graphic].width;
2677 int height = graphic_info[graphic].height;
2678 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2679 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2680 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2681 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2682 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2683 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2684 boolean draw_masked = graphic_info[graphic].draw_masked;
2686 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2688 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2690 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2694 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2695 inner_sx + (x - 1) * tile_width % inner_width);
2696 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2697 inner_sy + (y - 1) * tile_height % inner_height);
2700 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2703 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2707 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2708 int x, int y, int xsize, int ysize, int font_nr)
2710 int font_width = getFontWidth(font_nr);
2711 int font_height = getFontHeight(font_nr);
2713 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2714 font_width, font_height);
2717 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2719 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2720 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2721 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2722 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2723 boolean no_delay = (tape.warp_forward);
2724 unsigned int anim_delay = 0;
2725 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2726 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2727 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2728 int font_width = getFontWidth(font_nr);
2729 int font_height = getFontHeight(font_nr);
2730 int max_xsize = level.envelope[envelope_nr].xsize;
2731 int max_ysize = level.envelope[envelope_nr].ysize;
2732 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2733 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2734 int xend = max_xsize;
2735 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2736 int xstep = (xstart < xend ? 1 : 0);
2737 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2739 int end = MAX(xend - xstart, yend - ystart);
2742 for (i = start; i <= end; i++)
2744 int last_frame = end; // last frame of this "for" loop
2745 int x = xstart + i * xstep;
2746 int y = ystart + i * ystep;
2747 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2748 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2749 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2750 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2753 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2755 BlitScreenToBitmap(backbuffer);
2757 SetDrawtoField(DRAW_TO_BACKBUFFER);
2759 for (yy = 0; yy < ysize; yy++)
2760 for (xx = 0; xx < xsize; xx++)
2761 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2763 DrawTextBuffer(sx + font_width, sy + font_height,
2764 level.envelope[envelope_nr].text, font_nr, max_xsize,
2765 xsize - 2, ysize - 2, 0, mask_mode,
2766 level.envelope[envelope_nr].autowrap,
2767 level.envelope[envelope_nr].centered, FALSE);
2769 redraw_mask |= REDRAW_FIELD;
2772 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2776 void ShowEnvelope(int envelope_nr)
2778 int element = EL_ENVELOPE_1 + envelope_nr;
2779 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2780 int sound_opening = element_info[element].sound[ACTION_OPENING];
2781 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2782 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2783 boolean no_delay = (tape.warp_forward);
2784 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2785 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2786 int anim_mode = graphic_info[graphic].anim_mode;
2787 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2788 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2790 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2792 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2794 if (anim_mode == ANIM_DEFAULT)
2795 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2797 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2800 Delay(wait_delay_value);
2802 WaitForEventToContinue();
2804 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2806 if (anim_mode != ANIM_NONE)
2807 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2809 if (anim_mode == ANIM_DEFAULT)
2810 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2812 game.envelope_active = FALSE;
2814 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2816 redraw_mask |= REDRAW_FIELD;
2820 static void setRequestBasePosition(int *x, int *y)
2822 int sx_base, sy_base;
2824 if (request.x != -1)
2825 sx_base = request.x;
2826 else if (request.align == ALIGN_LEFT)
2828 else if (request.align == ALIGN_RIGHT)
2829 sx_base = SX + SXSIZE;
2831 sx_base = SX + SXSIZE / 2;
2833 if (request.y != -1)
2834 sy_base = request.y;
2835 else if (request.valign == VALIGN_TOP)
2837 else if (request.valign == VALIGN_BOTTOM)
2838 sy_base = SY + SYSIZE;
2840 sy_base = SY + SYSIZE / 2;
2846 static void setRequestPositionExt(int *x, int *y, int width, int height,
2847 boolean add_border_size)
2849 int border_size = request.border_size;
2850 int sx_base, sy_base;
2853 setRequestBasePosition(&sx_base, &sy_base);
2855 if (request.align == ALIGN_LEFT)
2857 else if (request.align == ALIGN_RIGHT)
2858 sx = sx_base - width;
2860 sx = sx_base - width / 2;
2862 if (request.valign == VALIGN_TOP)
2864 else if (request.valign == VALIGN_BOTTOM)
2865 sy = sy_base - height;
2867 sy = sy_base - height / 2;
2869 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2870 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2872 if (add_border_size)
2882 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2884 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2887 void DrawEnvelopeRequest(char *text)
2889 char *text_final = text;
2890 char *text_door_style = NULL;
2891 int graphic = IMG_BACKGROUND_REQUEST;
2892 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2893 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2894 int font_nr = FONT_REQUEST;
2895 int font_width = getFontWidth(font_nr);
2896 int font_height = getFontHeight(font_nr);
2897 int border_size = request.border_size;
2898 int line_spacing = request.line_spacing;
2899 int line_height = font_height + line_spacing;
2900 int max_text_width = request.width - 2 * border_size;
2901 int max_text_height = request.height - 2 * border_size;
2902 int line_length = max_text_width / font_width;
2903 int max_lines = max_text_height / line_height;
2904 int text_width = line_length * font_width;
2905 int width = request.width;
2906 int height = request.height;
2907 int tile_size = MAX(request.step_offset, 1);
2908 int x_steps = width / tile_size;
2909 int y_steps = height / tile_size;
2910 int sx_offset = border_size;
2911 int sy_offset = border_size;
2915 if (request.centered)
2916 sx_offset = (request.width - text_width) / 2;
2918 if (request.wrap_single_words && !request.autowrap)
2920 char *src_text_ptr, *dst_text_ptr;
2922 text_door_style = checked_malloc(2 * strlen(text) + 1);
2924 src_text_ptr = text;
2925 dst_text_ptr = text_door_style;
2927 while (*src_text_ptr)
2929 if (*src_text_ptr == ' ' ||
2930 *src_text_ptr == '?' ||
2931 *src_text_ptr == '!')
2932 *dst_text_ptr++ = '\n';
2934 if (*src_text_ptr != ' ')
2935 *dst_text_ptr++ = *src_text_ptr;
2940 *dst_text_ptr = '\0';
2942 text_final = text_door_style;
2945 setRequestPosition(&sx, &sy, FALSE);
2947 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2949 for (y = 0; y < y_steps; y++)
2950 for (x = 0; x < x_steps; x++)
2951 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2952 x, y, x_steps, y_steps,
2953 tile_size, tile_size);
2955 /* force DOOR font inside door area */
2956 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2958 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2959 line_length, -1, max_lines, line_spacing, mask_mode,
2960 request.autowrap, request.centered, FALSE);
2964 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2965 RedrawGadget(tool_gadget[i]);
2967 // store readily prepared envelope request for later use when animating
2968 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2970 if (text_door_style)
2971 free(text_door_style);
2974 void AnimateEnvelopeRequest(int anim_mode, int action)
2976 int graphic = IMG_BACKGROUND_REQUEST;
2977 boolean draw_masked = graphic_info[graphic].draw_masked;
2978 int delay_value_normal = request.step_delay;
2979 int delay_value_fast = delay_value_normal / 2;
2980 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2981 boolean no_delay = (tape.warp_forward);
2982 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2983 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2984 unsigned int anim_delay = 0;
2986 int tile_size = MAX(request.step_offset, 1);
2987 int max_xsize = request.width / tile_size;
2988 int max_ysize = request.height / tile_size;
2989 int max_xsize_inner = max_xsize - 2;
2990 int max_ysize_inner = max_ysize - 2;
2992 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2993 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2994 int xend = max_xsize_inner;
2995 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2996 int xstep = (xstart < xend ? 1 : 0);
2997 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2999 int end = MAX(xend - xstart, yend - ystart);
3002 if (setup.quick_doors)
3009 for (i = start; i <= end; i++)
3011 int last_frame = end; // last frame of this "for" loop
3012 int x = xstart + i * xstep;
3013 int y = ystart + i * ystep;
3014 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3015 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3016 int xsize_size_left = (xsize - 1) * tile_size;
3017 int ysize_size_top = (ysize - 1) * tile_size;
3018 int max_xsize_pos = (max_xsize - 1) * tile_size;
3019 int max_ysize_pos = (max_ysize - 1) * tile_size;
3020 int width = xsize * tile_size;
3021 int height = ysize * tile_size;
3026 setRequestPosition(&src_x, &src_y, FALSE);
3027 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3029 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3031 for (yy = 0; yy < 2; yy++)
3033 for (xx = 0; xx < 2; xx++)
3035 int src_xx = src_x + xx * max_xsize_pos;
3036 int src_yy = src_y + yy * max_ysize_pos;
3037 int dst_xx = dst_x + xx * xsize_size_left;
3038 int dst_yy = dst_y + yy * ysize_size_top;
3039 int xx_size = (xx ? tile_size : xsize_size_left);
3040 int yy_size = (yy ? tile_size : ysize_size_top);
3043 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3044 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3046 BlitBitmap(bitmap_db_store_2, backbuffer,
3047 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3051 redraw_mask |= REDRAW_FIELD;
3055 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3059 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3061 int graphic = IMG_BACKGROUND_REQUEST;
3062 int sound_opening = SND_REQUEST_OPENING;
3063 int sound_closing = SND_REQUEST_CLOSING;
3064 int anim_mode_1 = request.anim_mode; /* (higher priority) */
3065 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
3066 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3067 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3068 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3070 if (game_status == GAME_MODE_PLAYING)
3071 BlitScreenToBitmap(backbuffer);
3073 SetDrawtoField(DRAW_TO_BACKBUFFER);
3075 // SetDrawBackgroundMask(REDRAW_NONE);
3077 if (action == ACTION_OPENING)
3079 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3081 if (req_state & REQ_ASK)
3083 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3084 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3086 else if (req_state & REQ_CONFIRM)
3088 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3090 else if (req_state & REQ_PLAYER)
3092 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3093 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3094 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3095 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3098 DrawEnvelopeRequest(text);
3101 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
3103 if (action == ACTION_OPENING)
3105 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3107 if (anim_mode == ANIM_DEFAULT)
3108 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3110 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3114 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3116 if (anim_mode != ANIM_NONE)
3117 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3119 if (anim_mode == ANIM_DEFAULT)
3120 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3123 game.envelope_active = FALSE;
3125 if (action == ACTION_CLOSING)
3126 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3128 // SetDrawBackgroundMask(last_draw_background_mask);
3130 redraw_mask |= REDRAW_FIELD;
3134 if (action == ACTION_CLOSING &&
3135 game_status == GAME_MODE_PLAYING &&
3136 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3137 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3140 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3142 if (IS_MM_WALL(element))
3144 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3150 int graphic = el2preimg(element);
3152 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3153 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3158 void DrawLevel(int draw_background_mask)
3162 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3163 SetDrawBackgroundMask(draw_background_mask);
3167 for (x = BX1; x <= BX2; x++)
3168 for (y = BY1; y <= BY2; y++)
3169 DrawScreenField(x, y);
3171 redraw_mask |= REDRAW_FIELD;
3174 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3179 for (x = 0; x < size_x; x++)
3180 for (y = 0; y < size_y; y++)
3181 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3183 redraw_mask |= REDRAW_FIELD;
3186 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3190 for (x = 0; x < size_x; x++)
3191 for (y = 0; y < size_y; y++)
3192 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3194 redraw_mask |= REDRAW_FIELD;
3197 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3199 boolean show_level_border = (BorderElement != EL_EMPTY);
3200 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3201 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3202 int tile_size = preview.tile_size;
3203 int preview_width = preview.xsize * tile_size;
3204 int preview_height = preview.ysize * tile_size;
3205 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3206 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3207 int real_preview_width = real_preview_xsize * tile_size;
3208 int real_preview_height = real_preview_ysize * tile_size;
3209 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3210 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3213 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3216 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3218 dst_x += (preview_width - real_preview_width) / 2;
3219 dst_y += (preview_height - real_preview_height) / 2;
3221 for (x = 0; x < real_preview_xsize; x++)
3223 for (y = 0; y < real_preview_ysize; y++)
3225 int lx = from_x + x + (show_level_border ? -1 : 0);
3226 int ly = from_y + y + (show_level_border ? -1 : 0);
3227 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3228 getBorderElement(lx, ly));
3230 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3231 element, tile_size);
3235 redraw_mask |= REDRAW_FIELD;
3238 #define MICROLABEL_EMPTY 0
3239 #define MICROLABEL_LEVEL_NAME 1
3240 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3241 #define MICROLABEL_LEVEL_AUTHOR 3
3242 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3243 #define MICROLABEL_IMPORTED_FROM 5
3244 #define MICROLABEL_IMPORTED_BY_HEAD 6
3245 #define MICROLABEL_IMPORTED_BY 7
3247 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3249 int max_text_width = SXSIZE;
3250 int font_width = getFontWidth(font_nr);
3252 if (pos->align == ALIGN_CENTER)
3253 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3254 else if (pos->align == ALIGN_RIGHT)
3255 max_text_width = pos->x;
3257 max_text_width = SXSIZE - pos->x;
3259 return max_text_width / font_width;
3262 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3264 char label_text[MAX_OUTPUT_LINESIZE + 1];
3265 int max_len_label_text;
3266 int font_nr = pos->font;
3269 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3272 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3273 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3274 mode == MICROLABEL_IMPORTED_BY_HEAD)
3275 font_nr = pos->font_alt;
3277 max_len_label_text = getMaxTextLength(pos, font_nr);
3279 if (pos->size != -1)
3280 max_len_label_text = pos->size;
3282 for (i = 0; i < max_len_label_text; i++)
3283 label_text[i] = ' ';
3284 label_text[max_len_label_text] = '\0';
3286 if (strlen(label_text) > 0)
3287 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3290 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3291 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3292 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3293 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3294 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3295 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3296 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3297 max_len_label_text);
3298 label_text[max_len_label_text] = '\0';
3300 if (strlen(label_text) > 0)
3301 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3303 redraw_mask |= REDRAW_FIELD;
3306 static void DrawPreviewLevelLabel(int mode)
3308 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3311 static void DrawPreviewLevelInfo(int mode)
3313 if (mode == MICROLABEL_LEVEL_NAME)
3314 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3315 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3316 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3319 static void DrawPreviewLevelExt(boolean restart)
3321 static unsigned int scroll_delay = 0;
3322 static unsigned int label_delay = 0;
3323 static int from_x, from_y, scroll_direction;
3324 static int label_state, label_counter;
3325 unsigned int scroll_delay_value = preview.step_delay;
3326 boolean show_level_border = (BorderElement != EL_EMPTY);
3327 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3328 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3335 if (preview.anim_mode == ANIM_CENTERED)
3337 if (level_xsize > preview.xsize)
3338 from_x = (level_xsize - preview.xsize) / 2;
3339 if (level_ysize > preview.ysize)
3340 from_y = (level_ysize - preview.ysize) / 2;
3343 from_x += preview.xoffset;
3344 from_y += preview.yoffset;
3346 scroll_direction = MV_RIGHT;
3350 DrawPreviewLevelPlayfield(from_x, from_y);
3351 DrawPreviewLevelLabel(label_state);
3353 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3354 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3356 /* initialize delay counters */
3357 DelayReached(&scroll_delay, 0);
3358 DelayReached(&label_delay, 0);
3360 if (leveldir_current->name)
3362 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3363 char label_text[MAX_OUTPUT_LINESIZE + 1];
3364 int font_nr = pos->font;
3365 int max_len_label_text = getMaxTextLength(pos, font_nr);
3367 if (pos->size != -1)
3368 max_len_label_text = pos->size;
3370 strncpy(label_text, leveldir_current->name, max_len_label_text);
3371 label_text[max_len_label_text] = '\0';
3373 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3374 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3380 /* scroll preview level, if needed */
3381 if (preview.anim_mode != ANIM_NONE &&
3382 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3383 DelayReached(&scroll_delay, scroll_delay_value))
3385 switch (scroll_direction)
3390 from_x -= preview.step_offset;
3391 from_x = (from_x < 0 ? 0 : from_x);
3394 scroll_direction = MV_UP;
3398 if (from_x < level_xsize - preview.xsize)
3400 from_x += preview.step_offset;
3401 from_x = (from_x > level_xsize - preview.xsize ?
3402 level_xsize - preview.xsize : from_x);
3405 scroll_direction = MV_DOWN;
3411 from_y -= preview.step_offset;
3412 from_y = (from_y < 0 ? 0 : from_y);
3415 scroll_direction = MV_RIGHT;
3419 if (from_y < level_ysize - preview.ysize)
3421 from_y += preview.step_offset;
3422 from_y = (from_y > level_ysize - preview.ysize ?
3423 level_ysize - preview.ysize : from_y);
3426 scroll_direction = MV_LEFT;
3433 DrawPreviewLevelPlayfield(from_x, from_y);
3436 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3437 /* redraw micro level label, if needed */
3438 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3439 !strEqual(level.author, ANONYMOUS_NAME) &&
3440 !strEqual(level.author, leveldir_current->name) &&
3441 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3443 int max_label_counter = 23;
3445 if (leveldir_current->imported_from != NULL &&
3446 strlen(leveldir_current->imported_from) > 0)
3447 max_label_counter += 14;
3448 if (leveldir_current->imported_by != NULL &&
3449 strlen(leveldir_current->imported_by) > 0)
3450 max_label_counter += 14;
3452 label_counter = (label_counter + 1) % max_label_counter;
3453 label_state = (label_counter >= 0 && label_counter <= 7 ?
3454 MICROLABEL_LEVEL_NAME :
3455 label_counter >= 9 && label_counter <= 12 ?
3456 MICROLABEL_LEVEL_AUTHOR_HEAD :
3457 label_counter >= 14 && label_counter <= 21 ?
3458 MICROLABEL_LEVEL_AUTHOR :
3459 label_counter >= 23 && label_counter <= 26 ?
3460 MICROLABEL_IMPORTED_FROM_HEAD :
3461 label_counter >= 28 && label_counter <= 35 ?
3462 MICROLABEL_IMPORTED_FROM :
3463 label_counter >= 37 && label_counter <= 40 ?
3464 MICROLABEL_IMPORTED_BY_HEAD :
3465 label_counter >= 42 && label_counter <= 49 ?
3466 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3468 if (leveldir_current->imported_from == NULL &&
3469 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3470 label_state == MICROLABEL_IMPORTED_FROM))
3471 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3472 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3474 DrawPreviewLevelLabel(label_state);
3478 void DrawPreviewLevelInitial()
3480 DrawPreviewLevelExt(TRUE);
3483 void DrawPreviewLevelAnimation()
3485 DrawPreviewLevelExt(FALSE);
3488 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3489 int graphic, int sync_frame,
3492 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3494 if (mask_mode == USE_MASKING)
3495 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3497 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3500 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3501 int graphic, int sync_frame, int mask_mode)
3503 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3505 if (mask_mode == USE_MASKING)
3506 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3508 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3511 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3513 int lx = LEVELX(x), ly = LEVELY(y);
3515 if (!IN_SCR_FIELD(x, y))
3518 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3519 graphic, GfxFrame[lx][ly], NO_MASKING);
3521 MarkTileDirty(x, y);
3524 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3526 int lx = LEVELX(x), ly = LEVELY(y);
3528 if (!IN_SCR_FIELD(x, y))
3531 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3532 graphic, GfxFrame[lx][ly], NO_MASKING);
3533 MarkTileDirty(x, y);
3536 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3538 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3541 void DrawLevelElementAnimation(int x, int y, int element)
3543 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3545 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3548 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3550 int sx = SCREENX(x), sy = SCREENY(y);
3552 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3555 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3558 DrawGraphicAnimation(sx, sy, graphic);
3561 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3562 DrawLevelFieldCrumbled(x, y);
3564 if (GFX_CRUMBLED(Feld[x][y]))
3565 DrawLevelFieldCrumbled(x, y);
3569 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3571 int sx = SCREENX(x), sy = SCREENY(y);
3574 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3577 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3579 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3582 DrawGraphicAnimation(sx, sy, graphic);
3584 if (GFX_CRUMBLED(element))
3585 DrawLevelFieldCrumbled(x, y);
3588 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3590 if (player->use_murphy)
3592 /* this works only because currently only one player can be "murphy" ... */
3593 static int last_horizontal_dir = MV_LEFT;
3594 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3596 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3597 last_horizontal_dir = move_dir;
3599 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3601 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3603 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3609 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3612 static boolean equalGraphics(int graphic1, int graphic2)
3614 struct GraphicInfo *g1 = &graphic_info[graphic1];
3615 struct GraphicInfo *g2 = &graphic_info[graphic2];
3617 return (g1->bitmap == g2->bitmap &&
3618 g1->src_x == g2->src_x &&
3619 g1->src_y == g2->src_y &&
3620 g1->anim_frames == g2->anim_frames &&
3621 g1->anim_delay == g2->anim_delay &&
3622 g1->anim_mode == g2->anim_mode);
3625 void DrawAllPlayers()
3629 for (i = 0; i < MAX_PLAYERS; i++)
3630 if (stored_player[i].active)
3631 DrawPlayer(&stored_player[i]);
3634 void DrawPlayerField(int x, int y)
3636 if (!IS_PLAYER(x, y))
3639 DrawPlayer(PLAYERINFO(x, y));
3642 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3644 void DrawPlayer(struct PlayerInfo *player)
3646 int jx = player->jx;
3647 int jy = player->jy;
3648 int move_dir = player->MovDir;
3649 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3650 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3651 int last_jx = (player->is_moving ? jx - dx : jx);
3652 int last_jy = (player->is_moving ? jy - dy : jy);
3653 int next_jx = jx + dx;
3654 int next_jy = jy + dy;
3655 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3656 boolean player_is_opaque = FALSE;
3657 int sx = SCREENX(jx), sy = SCREENY(jy);
3658 int sxx = 0, syy = 0;
3659 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3661 int action = ACTION_DEFAULT;
3662 int last_player_graphic = getPlayerGraphic(player, move_dir);
3663 int last_player_frame = player->Frame;
3666 /* GfxElement[][] is set to the element the player is digging or collecting;
3667 remove also for off-screen player if the player is not moving anymore */
3668 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3669 GfxElement[jx][jy] = EL_UNDEFINED;
3671 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3675 if (!IN_LEV_FIELD(jx, jy))
3677 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3678 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3679 printf("DrawPlayerField(): This should never happen!\n");
3684 if (element == EL_EXPLOSION)
3687 action = (player->is_pushing ? ACTION_PUSHING :
3688 player->is_digging ? ACTION_DIGGING :
3689 player->is_collecting ? ACTION_COLLECTING :
3690 player->is_moving ? ACTION_MOVING :
3691 player->is_snapping ? ACTION_SNAPPING :
3692 player->is_dropping ? ACTION_DROPPING :
3693 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3695 if (player->is_waiting)
3696 move_dir = player->dir_waiting;
3698 InitPlayerGfxAnimation(player, action, move_dir);
3700 /* ----------------------------------------------------------------------- */
3701 /* draw things in the field the player is leaving, if needed */
3702 /* ----------------------------------------------------------------------- */
3704 if (player->is_moving)
3706 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3708 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3710 if (last_element == EL_DYNAMITE_ACTIVE ||
3711 last_element == EL_EM_DYNAMITE_ACTIVE ||
3712 last_element == EL_SP_DISK_RED_ACTIVE)
3713 DrawDynamite(last_jx, last_jy);
3715 DrawLevelFieldThruMask(last_jx, last_jy);
3717 else if (last_element == EL_DYNAMITE_ACTIVE ||
3718 last_element == EL_EM_DYNAMITE_ACTIVE ||
3719 last_element == EL_SP_DISK_RED_ACTIVE)
3720 DrawDynamite(last_jx, last_jy);
3722 /* !!! this is not enough to prevent flickering of players which are
3723 moving next to each others without a free tile between them -- this
3724 can only be solved by drawing all players layer by layer (first the
3725 background, then the foreground etc.) !!! => TODO */
3726 else if (!IS_PLAYER(last_jx, last_jy))
3727 DrawLevelField(last_jx, last_jy);
3730 DrawLevelField(last_jx, last_jy);
3733 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3734 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3737 if (!IN_SCR_FIELD(sx, sy))
3740 /* ----------------------------------------------------------------------- */
3741 /* draw things behind the player, if needed */
3742 /* ----------------------------------------------------------------------- */
3745 DrawLevelElement(jx, jy, Back[jx][jy]);
3746 else if (IS_ACTIVE_BOMB(element))
3747 DrawLevelElement(jx, jy, EL_EMPTY);
3750 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3752 int old_element = GfxElement[jx][jy];
3753 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3754 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3756 if (GFX_CRUMBLED(old_element))
3757 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3759 DrawGraphic(sx, sy, old_graphic, frame);
3761 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3762 player_is_opaque = TRUE;
3766 GfxElement[jx][jy] = EL_UNDEFINED;
3768 /* make sure that pushed elements are drawn with correct frame rate */
3769 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3771 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3772 GfxFrame[jx][jy] = player->StepFrame;
3774 DrawLevelField(jx, jy);
3778 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3779 /* ----------------------------------------------------------------------- */
3780 /* draw player himself */
3781 /* ----------------------------------------------------------------------- */
3783 graphic = getPlayerGraphic(player, move_dir);
3785 /* in the case of changed player action or direction, prevent the current
3786 animation frame from being restarted for identical animations */
3787 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3788 player->Frame = last_player_frame;
3790 frame = getGraphicAnimationFrame(graphic, player->Frame);
3794 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3795 sxx = player->GfxPos;
3797 syy = player->GfxPos;
3800 if (player_is_opaque)
3801 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3803 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3805 if (SHIELD_ON(player))
3807 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3808 IMG_SHIELD_NORMAL_ACTIVE);
3809 int frame = getGraphicAnimationFrame(graphic, -1);
3811 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3815 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3818 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3819 sxx = player->GfxPos;
3821 syy = player->GfxPos;
3825 /* ----------------------------------------------------------------------- */
3826 /* draw things the player is pushing, if needed */
3827 /* ----------------------------------------------------------------------- */
3829 if (player->is_pushing && player->is_moving)
3831 int px = SCREENX(jx), py = SCREENY(jy);
3832 int pxx = (TILEX - ABS(sxx)) * dx;
3833 int pyy = (TILEY - ABS(syy)) * dy;
3834 int gfx_frame = GfxFrame[jx][jy];
3840 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3842 element = Feld[next_jx][next_jy];
3843 gfx_frame = GfxFrame[next_jx][next_jy];
3846 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3848 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3849 frame = getGraphicAnimationFrame(graphic, sync_frame);
3851 /* draw background element under pushed element (like the Sokoban field) */
3852 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3854 /* this allows transparent pushing animation over non-black background */
3857 DrawLevelElement(jx, jy, Back[jx][jy]);
3859 DrawLevelElement(jx, jy, EL_EMPTY);
3861 if (Back[next_jx][next_jy])
3862 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3864 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3866 else if (Back[next_jx][next_jy])
3867 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3870 /* do not draw (EM style) pushing animation when pushing is finished */
3871 /* (two-tile animations usually do not contain start and end frame) */
3872 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3873 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3875 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3877 /* masked drawing is needed for EMC style (double) movement graphics */
3878 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3879 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3883 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3884 /* ----------------------------------------------------------------------- */
3885 /* draw player himself */
3886 /* ----------------------------------------------------------------------- */
3888 graphic = getPlayerGraphic(player, move_dir);
3890 /* in the case of changed player action or direction, prevent the current
3891 animation frame from being restarted for identical animations */
3892 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3893 player->Frame = last_player_frame;
3895 frame = getGraphicAnimationFrame(graphic, player->Frame);
3899 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3900 sxx = player->GfxPos;
3902 syy = player->GfxPos;
3905 if (player_is_opaque)
3906 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3908 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3910 if (SHIELD_ON(player))
3912 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3913 IMG_SHIELD_NORMAL_ACTIVE);
3914 int frame = getGraphicAnimationFrame(graphic, -1);
3916 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3920 /* ----------------------------------------------------------------------- */
3921 /* draw things in front of player (active dynamite or dynabombs) */
3922 /* ----------------------------------------------------------------------- */
3924 if (IS_ACTIVE_BOMB(element))
3926 graphic = el2img(element);
3927 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3929 if (game.emulation == EMU_SUPAPLEX)
3930 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3932 DrawGraphicThruMask(sx, sy, graphic, frame);
3935 if (player_is_moving && last_element == EL_EXPLOSION)
3937 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3938 GfxElement[last_jx][last_jy] : EL_EMPTY);
3939 int graphic = el_act2img(element, ACTION_EXPLODING);
3940 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3941 int phase = ExplodePhase[last_jx][last_jy] - 1;
3942 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3945 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3948 /* ----------------------------------------------------------------------- */
3949 /* draw elements the player is just walking/passing through/under */
3950 /* ----------------------------------------------------------------------- */
3952 if (player_is_moving)
3954 /* handle the field the player is leaving ... */
3955 if (IS_ACCESSIBLE_INSIDE(last_element))
3956 DrawLevelField(last_jx, last_jy);
3957 else if (IS_ACCESSIBLE_UNDER(last_element))
3958 DrawLevelFieldThruMask(last_jx, last_jy);
3961 /* do not redraw accessible elements if the player is just pushing them */
3962 if (!player_is_moving || !player->is_pushing)
3964 /* ... and the field the player is entering */
3965 if (IS_ACCESSIBLE_INSIDE(element))
3966 DrawLevelField(jx, jy);
3967 else if (IS_ACCESSIBLE_UNDER(element))
3968 DrawLevelFieldThruMask(jx, jy);
3971 MarkTileDirty(sx, sy);
3974 /* ------------------------------------------------------------------------- */
3976 void WaitForEventToContinue()
3978 boolean still_wait = TRUE;
3980 if (program.headless)
3983 /* simulate releasing mouse button over last gadget, if still pressed */
3985 HandleGadgets(-1, -1, 0);
3987 button_status = MB_RELEASED;
3995 if (NextValidEvent(&event))
3999 case EVENT_BUTTONPRESS:
4000 case EVENT_KEYPRESS:
4001 #if defined(TARGET_SDL2)
4002 case SDL_CONTROLLERBUTTONDOWN:
4004 case SDL_JOYBUTTONDOWN:
4008 case EVENT_KEYRELEASE:
4009 ClearPlayerAction();
4013 HandleOtherEvents(&event);
4017 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4026 #define MAX_REQUEST_LINES 13
4027 #define MAX_REQUEST_LINE_FONT1_LEN 7
4028 #define MAX_REQUEST_LINE_FONT2_LEN 10
4030 static int RequestHandleEvents(unsigned int req_state)
4032 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
4033 local_player->LevelSolved_GameEnd);
4034 int width = request.width;
4035 int height = request.height;
4039 setRequestPosition(&sx, &sy, FALSE);
4041 button_status = MB_RELEASED;
4043 request_gadget_id = -1;
4050 /* the MM game engine does not use a special (scrollable) field buffer */
4051 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4052 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4054 HandleGameActions();
4056 SetDrawtoField(DRAW_TO_BACKBUFFER);
4058 if (global.use_envelope_request)
4060 /* copy current state of request area to middle of playfield area */
4061 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4069 while (NextValidEvent(&event))
4073 case EVENT_BUTTONPRESS:
4074 case EVENT_BUTTONRELEASE:
4075 case EVENT_MOTIONNOTIFY:
4079 if (event.type == EVENT_MOTIONNOTIFY)
4084 motion_status = TRUE;
4085 mx = ((MotionEvent *) &event)->x;
4086 my = ((MotionEvent *) &event)->y;
4090 motion_status = FALSE;
4091 mx = ((ButtonEvent *) &event)->x;
4092 my = ((ButtonEvent *) &event)->y;
4093 if (event.type == EVENT_BUTTONPRESS)
4094 button_status = ((ButtonEvent *) &event)->button;
4096 button_status = MB_RELEASED;
4099 /* this sets 'request_gadget_id' */
4100 HandleGadgets(mx, my, button_status);
4102 switch (request_gadget_id)
4104 case TOOL_CTRL_ID_YES:
4107 case TOOL_CTRL_ID_NO:
4110 case TOOL_CTRL_ID_CONFIRM:
4111 result = TRUE | FALSE;
4114 case TOOL_CTRL_ID_PLAYER_1:
4117 case TOOL_CTRL_ID_PLAYER_2:
4120 case TOOL_CTRL_ID_PLAYER_3:
4123 case TOOL_CTRL_ID_PLAYER_4:
4134 #if defined(TARGET_SDL2)
4135 case SDL_WINDOWEVENT:
4136 HandleWindowEvent((WindowEvent *) &event);
4139 case SDL_APP_WILLENTERBACKGROUND:
4140 case SDL_APP_DIDENTERBACKGROUND:
4141 case SDL_APP_WILLENTERFOREGROUND:
4142 case SDL_APP_DIDENTERFOREGROUND:
4143 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4147 case EVENT_KEYPRESS:
4149 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4154 if (req_state & REQ_CONFIRM)
4159 #if defined(TARGET_SDL2)
4162 #if defined(KSYM_Rewind)
4163 case KSYM_Rewind: /* for Amazon Fire TV remote */
4170 #if defined(TARGET_SDL2)
4172 #if defined(KSYM_FastForward)
4173 case KSYM_FastForward: /* for Amazon Fire TV remote */
4180 HandleKeysDebug(key);
4184 if (req_state & REQ_PLAYER)
4190 case EVENT_KEYRELEASE:
4191 ClearPlayerAction();
4194 #if defined(TARGET_SDL2)
4195 case SDL_CONTROLLERBUTTONDOWN:
4196 switch (event.cbutton.button)
4198 case SDL_CONTROLLER_BUTTON_A:
4199 case SDL_CONTROLLER_BUTTON_X:
4200 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4204 case SDL_CONTROLLER_BUTTON_B:
4205 case SDL_CONTROLLER_BUTTON_Y:
4206 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4207 case SDL_CONTROLLER_BUTTON_BACK:
4212 if (req_state & REQ_PLAYER)
4217 case SDL_CONTROLLERBUTTONUP:
4218 HandleJoystickEvent(&event);
4219 ClearPlayerAction();
4224 HandleOtherEvents(&event);
4229 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4231 int joy = AnyJoystick();
4233 if (joy & JOY_BUTTON_1)
4235 else if (joy & JOY_BUTTON_2)
4241 if (global.use_envelope_request)
4243 /* copy back current state of pressed buttons inside request area */
4244 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4254 static boolean RequestDoor(char *text, unsigned int req_state)
4256 unsigned int old_door_state;
4257 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4258 int font_nr = FONT_TEXT_2;
4263 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4265 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4266 font_nr = FONT_TEXT_1;
4269 if (game_status == GAME_MODE_PLAYING)
4270 BlitScreenToBitmap(backbuffer);
4272 /* disable deactivated drawing when quick-loading level tape recording */
4273 if (tape.playing && tape.deactivate_display)
4274 TapeDeactivateDisplayOff(TRUE);
4276 SetMouseCursor(CURSOR_DEFAULT);
4278 #if defined(NETWORK_AVALIABLE)
4279 /* pause network game while waiting for request to answer */
4280 if (options.network &&
4281 game_status == GAME_MODE_PLAYING &&
4282 req_state & REQUEST_WAIT_FOR_INPUT)
4283 SendToServer_PausePlaying();
4286 old_door_state = GetDoorState();
4288 /* simulate releasing mouse button over last gadget, if still pressed */
4290 HandleGadgets(-1, -1, 0);
4294 /* draw released gadget before proceeding */
4297 if (old_door_state & DOOR_OPEN_1)
4299 CloseDoor(DOOR_CLOSE_1);
4301 /* save old door content */
4302 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4303 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4306 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4307 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4309 /* clear door drawing field */
4310 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4312 /* force DOOR font inside door area */
4313 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4315 /* write text for request */
4316 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4318 char text_line[max_request_line_len + 1];
4324 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4326 tc = *(text_ptr + tx);
4327 // if (!tc || tc == ' ')
4328 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4332 if ((tc == '?' || tc == '!') && tl == 0)
4342 strncpy(text_line, text_ptr, tl);
4345 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4346 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4347 text_line, font_nr);
4349 text_ptr += tl + (tc == ' ' ? 1 : 0);
4350 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4355 if (req_state & REQ_ASK)
4357 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4358 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4360 else if (req_state & REQ_CONFIRM)
4362 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4364 else if (req_state & REQ_PLAYER)
4366 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4367 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4368 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4369 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4372 /* copy request gadgets to door backbuffer */
4373 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4375 OpenDoor(DOOR_OPEN_1);
4377 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4379 if (game_status == GAME_MODE_PLAYING)
4381 SetPanelBackground();
4382 SetDrawBackgroundMask(REDRAW_DOOR_1);
4386 SetDrawBackgroundMask(REDRAW_FIELD);
4392 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4394 // ---------- handle request buttons ----------
4395 result = RequestHandleEvents(req_state);
4399 if (!(req_state & REQ_STAY_OPEN))
4401 CloseDoor(DOOR_CLOSE_1);
4403 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4404 (req_state & REQ_REOPEN))
4405 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4410 if (game_status == GAME_MODE_PLAYING)
4412 SetPanelBackground();
4413 SetDrawBackgroundMask(REDRAW_DOOR_1);
4417 SetDrawBackgroundMask(REDRAW_FIELD);
4420 #if defined(NETWORK_AVALIABLE)
4421 /* continue network game after request */
4422 if (options.network &&
4423 game_status == GAME_MODE_PLAYING &&
4424 req_state & REQUEST_WAIT_FOR_INPUT)
4425 SendToServer_ContinuePlaying();
4428 /* restore deactivated drawing when quick-loading level tape recording */
4429 if (tape.playing && tape.deactivate_display)
4430 TapeDeactivateDisplayOn();
4435 static boolean RequestEnvelope(char *text, unsigned int req_state)
4439 if (game_status == GAME_MODE_PLAYING)
4440 BlitScreenToBitmap(backbuffer);
4442 /* disable deactivated drawing when quick-loading level tape recording */
4443 if (tape.playing && tape.deactivate_display)
4444 TapeDeactivateDisplayOff(TRUE);
4446 SetMouseCursor(CURSOR_DEFAULT);
4448 #if defined(NETWORK_AVALIABLE)
4449 /* pause network game while waiting for request to answer */
4450 if (options.network &&
4451 game_status == GAME_MODE_PLAYING &&
4452 req_state & REQUEST_WAIT_FOR_INPUT)
4453 SendToServer_PausePlaying();
4456 /* simulate releasing mouse button over last gadget, if still pressed */
4458 HandleGadgets(-1, -1, 0);
4462 // (replace with setting corresponding request background)
4463 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4464 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4466 /* clear door drawing field */
4467 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4469 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4471 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4473 if (game_status == GAME_MODE_PLAYING)
4475 SetPanelBackground();
4476 SetDrawBackgroundMask(REDRAW_DOOR_1);
4480 SetDrawBackgroundMask(REDRAW_FIELD);
4486 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4488 // ---------- handle request buttons ----------
4489 result = RequestHandleEvents(req_state);
4493 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4497 if (game_status == GAME_MODE_PLAYING)
4499 SetPanelBackground();
4500 SetDrawBackgroundMask(REDRAW_DOOR_1);
4504 SetDrawBackgroundMask(REDRAW_FIELD);
4507 #if defined(NETWORK_AVALIABLE)
4508 /* continue network game after request */
4509 if (options.network &&
4510 game_status == GAME_MODE_PLAYING &&
4511 req_state & REQUEST_WAIT_FOR_INPUT)
4512 SendToServer_ContinuePlaying();
4515 /* restore deactivated drawing when quick-loading level tape recording */
4516 if (tape.playing && tape.deactivate_display)
4517 TapeDeactivateDisplayOn();
4522 boolean Request(char *text, unsigned int req_state)
4524 boolean overlay_active = GetOverlayActive();
4527 SetOverlayActive(FALSE);
4529 if (global.use_envelope_request)
4530 result = RequestEnvelope(text, req_state);
4532 result = RequestDoor(text, req_state);
4534 SetOverlayActive(overlay_active);
4539 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4541 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4542 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4545 if (dpo1->sort_priority != dpo2->sort_priority)
4546 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4548 compare_result = dpo1->nr - dpo2->nr;
4550 return compare_result;
4553 void InitGraphicCompatibilityInfo_Doors()
4559 struct DoorInfo *door;
4563 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4564 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4566 { -1, -1, -1, NULL }
4568 struct Rect door_rect_list[] =
4570 { DX, DY, DXSIZE, DYSIZE },
4571 { VX, VY, VXSIZE, VYSIZE }
4575 for (i = 0; doors[i].door_token != -1; i++)
4577 int door_token = doors[i].door_token;
4578 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4579 int part_1 = doors[i].part_1;
4580 int part_8 = doors[i].part_8;
4581 int part_2 = part_1 + 1;
4582 int part_3 = part_1 + 2;
4583 struct DoorInfo *door = doors[i].door;
4584 struct Rect *door_rect = &door_rect_list[door_index];
4585 boolean door_gfx_redefined = FALSE;
4587 /* check if any door part graphic definitions have been redefined */
4589 for (j = 0; door_part_controls[j].door_token != -1; j++)
4591 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4592 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4594 if (dpc->door_token == door_token && fi->redefined)
4595 door_gfx_redefined = TRUE;
4598 /* check for old-style door graphic/animation modifications */
4600 if (!door_gfx_redefined)
4602 if (door->anim_mode & ANIM_STATIC_PANEL)
4604 door->panel.step_xoffset = 0;
4605 door->panel.step_yoffset = 0;
4608 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4610 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4611 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4612 int num_door_steps, num_panel_steps;
4614 /* remove door part graphics other than the two default wings */
4616 for (j = 0; door_part_controls[j].door_token != -1; j++)
4618 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4619 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4621 if (dpc->graphic >= part_3 &&
4622 dpc->graphic <= part_8)
4626 /* set graphics and screen positions of the default wings */
4628 g_part_1->width = door_rect->width;
4629 g_part_1->height = door_rect->height;
4630 g_part_2->width = door_rect->width;
4631 g_part_2->height = door_rect->height;
4632 g_part_2->src_x = door_rect->width;
4633 g_part_2->src_y = g_part_1->src_y;
4635 door->part_2.x = door->part_1.x;
4636 door->part_2.y = door->part_1.y;
4638 if (door->width != -1)
4640 g_part_1->width = door->width;
4641 g_part_2->width = door->width;
4643 // special treatment for graphics and screen position of right wing
4644 g_part_2->src_x += door_rect->width - door->width;
4645 door->part_2.x += door_rect->width - door->width;
4648 if (door->height != -1)
4650 g_part_1->height = door->height;
4651 g_part_2->height = door->height;
4653 // special treatment for graphics and screen position of bottom wing
4654 g_part_2->src_y += door_rect->height - door->height;
4655 door->part_2.y += door_rect->height - door->height;
4658 /* set animation delays for the default wings and panels */
4660 door->part_1.step_delay = door->step_delay;
4661 door->part_2.step_delay = door->step_delay;
4662 door->panel.step_delay = door->step_delay;
4664 /* set animation draw order for the default wings */
4666 door->part_1.sort_priority = 2; /* draw left wing over ... */
4667 door->part_2.sort_priority = 1; /* ... right wing */
4669 /* set animation draw offset for the default wings */
4671 if (door->anim_mode & ANIM_HORIZONTAL)
4673 door->part_1.step_xoffset = door->step_offset;
4674 door->part_1.step_yoffset = 0;
4675 door->part_2.step_xoffset = door->step_offset * -1;
4676 door->part_2.step_yoffset = 0;
4678 num_door_steps = g_part_1->width / door->step_offset;
4680 else // ANIM_VERTICAL
4682 door->part_1.step_xoffset = 0;
4683 door->part_1.step_yoffset = door->step_offset;
4684 door->part_2.step_xoffset = 0;
4685 door->part_2.step_yoffset = door->step_offset * -1;
4687 num_door_steps = g_part_1->height / door->step_offset;
4690 /* set animation draw offset for the default panels */
4692 if (door->step_offset > 1)
4694 num_panel_steps = 2 * door_rect->height / door->step_offset;
4695 door->panel.start_step = num_panel_steps - num_door_steps;
4696 door->panel.start_step_closing = door->panel.start_step;
4700 num_panel_steps = door_rect->height / door->step_offset;
4701 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4702 door->panel.start_step_closing = door->panel.start_step;
4703 door->panel.step_delay *= 2;
4714 for (i = 0; door_part_controls[i].door_token != -1; i++)
4716 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4717 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4719 /* initialize "start_step_opening" and "start_step_closing", if needed */
4720 if (dpc->pos->start_step_opening == 0 &&
4721 dpc->pos->start_step_closing == 0)
4723 // dpc->pos->start_step_opening = dpc->pos->start_step;
4724 dpc->pos->start_step_closing = dpc->pos->start_step;
4727 /* fill structure for door part draw order (sorted below) */
4729 dpo->sort_priority = dpc->pos->sort_priority;
4732 /* sort door part controls according to sort_priority and graphic number */
4733 qsort(door_part_order, MAX_DOOR_PARTS,
4734 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4737 unsigned int OpenDoor(unsigned int door_state)
4739 if (door_state & DOOR_COPY_BACK)
4741 if (door_state & DOOR_OPEN_1)
4742 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4743 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4745 if (door_state & DOOR_OPEN_2)
4746 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4747 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4749 door_state &= ~DOOR_COPY_BACK;
4752 return MoveDoor(door_state);
4755 unsigned int CloseDoor(unsigned int door_state)
4757 unsigned int old_door_state = GetDoorState();
4759 if (!(door_state & DOOR_NO_COPY_BACK))
4761 if (old_door_state & DOOR_OPEN_1)
4762 BlitBitmap(backbuffer, bitmap_db_door_1,
4763 DX, DY, DXSIZE, DYSIZE, 0, 0);
4765 if (old_door_state & DOOR_OPEN_2)
4766 BlitBitmap(backbuffer, bitmap_db_door_2,
4767 VX, VY, VXSIZE, VYSIZE, 0, 0);
4769 door_state &= ~DOOR_NO_COPY_BACK;
4772 return MoveDoor(door_state);
4775 unsigned int GetDoorState()
4777 return MoveDoor(DOOR_GET_STATE);
4780 unsigned int SetDoorState(unsigned int door_state)
4782 return MoveDoor(door_state | DOOR_SET_STATE);
4785 int euclid(int a, int b)
4787 return (b ? euclid(b, a % b) : a);
4790 unsigned int MoveDoor(unsigned int door_state)
4792 struct Rect door_rect_list[] =
4794 { DX, DY, DXSIZE, DYSIZE },
4795 { VX, VY, VXSIZE, VYSIZE }
4797 static int door1 = DOOR_CLOSE_1;
4798 static int door2 = DOOR_CLOSE_2;
4799 unsigned int door_delay = 0;
4800 unsigned int door_delay_value;
4803 if (door_state == DOOR_GET_STATE)
4804 return (door1 | door2);
4806 if (door_state & DOOR_SET_STATE)
4808 if (door_state & DOOR_ACTION_1)
4809 door1 = door_state & DOOR_ACTION_1;
4810 if (door_state & DOOR_ACTION_2)
4811 door2 = door_state & DOOR_ACTION_2;
4813 return (door1 | door2);
4816 if (!(door_state & DOOR_FORCE_REDRAW))
4818 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4819 door_state &= ~DOOR_OPEN_1;
4820 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4821 door_state &= ~DOOR_CLOSE_1;
4822 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4823 door_state &= ~DOOR_OPEN_2;
4824 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4825 door_state &= ~DOOR_CLOSE_2;
4828 if (global.autoplay_leveldir)
4830 door_state |= DOOR_NO_DELAY;
4831 door_state &= ~DOOR_CLOSE_ALL;
4834 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
4835 door_state |= DOOR_NO_DELAY;
4837 if (door_state & DOOR_ACTION)
4839 boolean door_panel_drawn[NUM_DOORS];
4840 boolean panel_has_doors[NUM_DOORS];
4841 boolean door_part_skip[MAX_DOOR_PARTS];
4842 boolean door_part_done[MAX_DOOR_PARTS];
4843 boolean door_part_done_all;
4844 int num_steps[MAX_DOOR_PARTS];
4845 int max_move_delay = 0; // delay for complete animations of all doors
4846 int max_step_delay = 0; // delay (ms) between two animation frames
4847 int num_move_steps = 0; // number of animation steps for all doors
4848 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4849 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4850 int current_move_delay = 0;
4854 for (i = 0; i < NUM_DOORS; i++)
4855 panel_has_doors[i] = FALSE;
4857 for (i = 0; i < MAX_DOOR_PARTS; i++)
4859 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4860 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4861 int door_token = dpc->door_token;
4863 door_part_done[i] = FALSE;
4864 door_part_skip[i] = (!(door_state & door_token) ||
4868 for (i = 0; i < MAX_DOOR_PARTS; i++)
4870 int nr = door_part_order[i].nr;
4871 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4872 struct DoorPartPosInfo *pos = dpc->pos;
4873 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4874 int door_token = dpc->door_token;
4875 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4876 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4877 int step_xoffset = ABS(pos->step_xoffset);
4878 int step_yoffset = ABS(pos->step_yoffset);
4879 int step_delay = pos->step_delay;
4880 int current_door_state = door_state & door_token;
4881 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4882 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4883 boolean part_opening = (is_panel ? door_closing : door_opening);
4884 int start_step = (part_opening ? pos->start_step_opening :
4885 pos->start_step_closing);
4886 float move_xsize = (step_xoffset ? g->width : 0);
4887 float move_ysize = (step_yoffset ? g->height : 0);
4888 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4889 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4890 int move_steps = (move_xsteps && move_ysteps ?
4891 MIN(move_xsteps, move_ysteps) :
4892 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4893 int move_delay = move_steps * step_delay;
4895 if (door_part_skip[nr])
4898 max_move_delay = MAX(max_move_delay, move_delay);
4899 max_step_delay = (max_step_delay == 0 ? step_delay :
4900 euclid(max_step_delay, step_delay));
4901 num_steps[nr] = move_steps;
4905 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4907 panel_has_doors[door_index] = TRUE;
4911 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4913 num_move_steps = max_move_delay / max_step_delay;
4914 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4916 door_delay_value = max_step_delay;
4918 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4920 start = num_move_steps - 1;
4924 /* opening door sound has priority over simultaneously closing door */
4925 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4927 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4929 if (door_state & DOOR_OPEN_1)
4930 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
4931 if (door_state & DOOR_OPEN_2)
4932 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
4934 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4936 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4938 if (door_state & DOOR_CLOSE_1)
4939 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
4940 if (door_state & DOOR_CLOSE_2)
4941 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
4945 for (k = start; k < num_move_steps; k++)
4947 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4949 door_part_done_all = TRUE;
4951 for (i = 0; i < NUM_DOORS; i++)
4952 door_panel_drawn[i] = FALSE;
4954 for (i = 0; i < MAX_DOOR_PARTS; i++)
4956 int nr = door_part_order[i].nr;
4957 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4958 struct DoorPartPosInfo *pos = dpc->pos;
4959 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4960 int door_token = dpc->door_token;
4961 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4962 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4963 boolean is_panel_and_door_has_closed = FALSE;
4964 struct Rect *door_rect = &door_rect_list[door_index];
4965 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4967 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4968 int current_door_state = door_state & door_token;
4969 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4970 boolean door_closing = !door_opening;
4971 boolean part_opening = (is_panel ? door_closing : door_opening);
4972 boolean part_closing = !part_opening;
4973 int start_step = (part_opening ? pos->start_step_opening :
4974 pos->start_step_closing);
4975 int step_delay = pos->step_delay;
4976 int step_factor = step_delay / max_step_delay;
4977 int k1 = (step_factor ? k / step_factor + 1 : k);
4978 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4979 int kk = MAX(0, k2);
4982 int src_x, src_y, src_xx, src_yy;
4983 int dst_x, dst_y, dst_xx, dst_yy;
4986 if (door_part_skip[nr])
4989 if (!(door_state & door_token))
4997 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4998 int kk_door = MAX(0, k2_door);
4999 int sync_frame = kk_door * door_delay_value;
5000 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5002 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5003 &g_src_x, &g_src_y);
5008 if (!door_panel_drawn[door_index])
5010 ClearRectangle(drawto, door_rect->x, door_rect->y,
5011 door_rect->width, door_rect->height);
5013 door_panel_drawn[door_index] = TRUE;
5016 // draw opening or closing door parts
5018 if (pos->step_xoffset < 0) // door part on right side
5021 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5024 if (dst_xx + width > door_rect->width)
5025 width = door_rect->width - dst_xx;
5027 else // door part on left side
5030 dst_xx = pos->x - kk * pos->step_xoffset;
5034 src_xx = ABS(dst_xx);
5038 width = g->width - src_xx;
5040 if (width > door_rect->width)
5041 width = door_rect->width;
5043 // printf("::: k == %d [%d] \n", k, start_step);
5046 if (pos->step_yoffset < 0) // door part on bottom side
5049 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5052 if (dst_yy + height > door_rect->height)
5053 height = door_rect->height - dst_yy;
5055 else // door part on top side
5058 dst_yy = pos->y - kk * pos->step_yoffset;
5062 src_yy = ABS(dst_yy);
5066 height = g->height - src_yy;
5069 src_x = g_src_x + src_xx;
5070 src_y = g_src_y + src_yy;
5072 dst_x = door_rect->x + dst_xx;
5073 dst_y = door_rect->y + dst_yy;
5075 is_panel_and_door_has_closed =
5078 panel_has_doors[door_index] &&
5079 k >= num_move_steps_doors_only - 1);
5081 if (width >= 0 && width <= g->width &&
5082 height >= 0 && height <= g->height &&
5083 !is_panel_and_door_has_closed)
5085 if (is_panel || !pos->draw_masked)
5086 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5089 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5093 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5095 if ((part_opening && (width < 0 || height < 0)) ||
5096 (part_closing && (width >= g->width && height >= g->height)))
5097 door_part_done[nr] = TRUE;
5099 // continue door part animations, but not panel after door has closed
5100 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5101 door_part_done_all = FALSE;
5104 if (!(door_state & DOOR_NO_DELAY))
5108 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5110 current_move_delay += max_step_delay;
5112 /* prevent OS (Windows) from complaining about program not responding */
5116 if (door_part_done_all)
5120 if (!(door_state & DOOR_NO_DELAY))
5122 /* wait for specified door action post delay */
5123 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5124 Delay(MAX(door_1.post_delay, door_2.post_delay));
5125 else if (door_state & DOOR_ACTION_1)
5126 Delay(door_1.post_delay);
5127 else if (door_state & DOOR_ACTION_2)
5128 Delay(door_2.post_delay);
5132 if (door_state & DOOR_ACTION_1)
5133 door1 = door_state & DOOR_ACTION_1;
5134 if (door_state & DOOR_ACTION_2)
5135 door2 = door_state & DOOR_ACTION_2;
5137 // draw masked border over door area
5138 DrawMaskedBorder(REDRAW_DOOR_1);
5139 DrawMaskedBorder(REDRAW_DOOR_2);
5141 return (door1 | door2);
5144 static boolean useSpecialEditorDoor()
5146 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5147 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5149 // do not draw special editor door if editor border defined or redefined
5150 if (graphic_info[graphic].bitmap != NULL || redefined)
5153 // do not draw special editor door if global border defined to be empty
5154 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5157 // do not draw special editor door if viewport definitions do not match
5161 EY + EYSIZE != VY + VYSIZE)
5167 void DrawSpecialEditorDoor()
5169 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5170 int top_border_width = gfx1->width;
5171 int top_border_height = gfx1->height;
5172 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5173 int ex = EX - outer_border;
5174 int ey = EY - outer_border;
5175 int vy = VY - outer_border;
5176 int exsize = EXSIZE + 2 * outer_border;
5178 if (!useSpecialEditorDoor())
5181 /* draw bigger level editor toolbox window */
5182 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5183 top_border_width, top_border_height, ex, ey - top_border_height);
5184 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5185 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5187 redraw_mask |= REDRAW_ALL;
5190 void UndrawSpecialEditorDoor()
5192 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5193 int top_border_width = gfx1->width;
5194 int top_border_height = gfx1->height;
5195 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5196 int ex = EX - outer_border;
5197 int ey = EY - outer_border;
5198 int ey_top = ey - top_border_height;
5199 int exsize = EXSIZE + 2 * outer_border;
5200 int eysize = EYSIZE + 2 * outer_border;
5202 if (!useSpecialEditorDoor())
5205 /* draw normal tape recorder window */
5206 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5208 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5209 ex, ey_top, top_border_width, top_border_height,
5211 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5212 ex, ey, exsize, eysize, ex, ey);
5216 // if screen background is set to "[NONE]", clear editor toolbox window
5217 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5218 ClearRectangle(drawto, ex, ey, exsize, eysize);
5221 redraw_mask |= REDRAW_ALL;
5225 /* ---------- new tool button stuff ---------------------------------------- */
5230 struct TextPosInfo *pos;
5233 } toolbutton_info[NUM_TOOL_BUTTONS] =
5236 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5237 TOOL_CTRL_ID_YES, "yes"
5240 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5241 TOOL_CTRL_ID_NO, "no"
5244 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5245 TOOL_CTRL_ID_CONFIRM, "confirm"
5248 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5249 TOOL_CTRL_ID_PLAYER_1, "player 1"
5252 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5253 TOOL_CTRL_ID_PLAYER_2, "player 2"
5256 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5257 TOOL_CTRL_ID_PLAYER_3, "player 3"
5260 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5261 TOOL_CTRL_ID_PLAYER_4, "player 4"
5265 void CreateToolButtons()
5269 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5271 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5272 struct TextPosInfo *pos = toolbutton_info[i].pos;
5273 struct GadgetInfo *gi;
5274 Bitmap *deco_bitmap = None;
5275 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5276 unsigned int event_mask = GD_EVENT_RELEASED;
5279 int gd_x = gfx->src_x;
5280 int gd_y = gfx->src_y;
5281 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5282 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5285 if (global.use_envelope_request)
5286 setRequestPosition(&dx, &dy, TRUE);
5288 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5290 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5292 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5293 pos->size, &deco_bitmap, &deco_x, &deco_y);
5294 deco_xpos = (gfx->width - pos->size) / 2;
5295 deco_ypos = (gfx->height - pos->size) / 2;
5298 gi = CreateGadget(GDI_CUSTOM_ID, id,
5299 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5300 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5301 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5302 GDI_WIDTH, gfx->width,
5303 GDI_HEIGHT, gfx->height,
5304 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5305 GDI_STATE, GD_BUTTON_UNPRESSED,
5306 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5307 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5308 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5309 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5310 GDI_DECORATION_SIZE, pos->size, pos->size,
5311 GDI_DECORATION_SHIFTING, 1, 1,
5312 GDI_DIRECT_DRAW, FALSE,
5313 GDI_EVENT_MASK, event_mask,
5314 GDI_CALLBACK_ACTION, HandleToolButtons,
5318 Error(ERR_EXIT, "cannot create gadget");
5320 tool_gadget[id] = gi;
5324 void FreeToolButtons()
5328 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5329 FreeGadget(tool_gadget[i]);
5332 static void UnmapToolButtons()
5336 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5337 UnmapGadget(tool_gadget[i]);
5340 static void HandleToolButtons(struct GadgetInfo *gi)
5342 request_gadget_id = gi->custom_id;
5345 static struct Mapping_EM_to_RND_object
5348 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5349 boolean is_backside; /* backside of moving element */
5355 em_object_mapping_list[] =
5358 Xblank, TRUE, FALSE,
5362 Yacid_splash_eB, FALSE, FALSE,
5363 EL_ACID_SPLASH_RIGHT, -1, -1
5366 Yacid_splash_wB, FALSE, FALSE,
5367 EL_ACID_SPLASH_LEFT, -1, -1
5370 #ifdef EM_ENGINE_BAD_ROLL
5372 Xstone_force_e, FALSE, FALSE,
5373 EL_ROCK, -1, MV_BIT_RIGHT
5376 Xstone_force_w, FALSE, FALSE,
5377 EL_ROCK, -1, MV_BIT_LEFT
5380 Xnut_force_e, FALSE, FALSE,
5381 EL_NUT, -1, MV_BIT_RIGHT
5384 Xnut_force_w, FALSE, FALSE,
5385 EL_NUT, -1, MV_BIT_LEFT
5388 Xspring_force_e, FALSE, FALSE,
5389 EL_SPRING, -1, MV_BIT_RIGHT
5392 Xspring_force_w, FALSE, FALSE,
5393 EL_SPRING, -1, MV_BIT_LEFT
5396 Xemerald_force_e, FALSE, FALSE,
5397 EL_EMERALD, -1, MV_BIT_RIGHT
5400 Xemerald_force_w, FALSE, FALSE,
5401 EL_EMERALD, -1, MV_BIT_LEFT
5404 Xdiamond_force_e, FALSE, FALSE,
5405 EL_DIAMOND, -1, MV_BIT_RIGHT
5408 Xdiamond_force_w, FALSE, FALSE,
5409 EL_DIAMOND, -1, MV_BIT_LEFT
5412 Xbomb_force_e, FALSE, FALSE,
5413 EL_BOMB, -1, MV_BIT_RIGHT
5416 Xbomb_force_w, FALSE, FALSE,
5417 EL_BOMB, -1, MV_BIT_LEFT
5419 #endif /* EM_ENGINE_BAD_ROLL */
5422 Xstone, TRUE, FALSE,
5426 Xstone_pause, FALSE, FALSE,
5430 Xstone_fall, FALSE, FALSE,
5434 Ystone_s, FALSE, FALSE,
5435 EL_ROCK, ACTION_FALLING, -1
5438 Ystone_sB, FALSE, TRUE,
5439 EL_ROCK, ACTION_FALLING, -1
5442 Ystone_e, FALSE, FALSE,
5443 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5446 Ystone_eB, FALSE, TRUE,
5447 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5450 Ystone_w, FALSE, FALSE,
5451 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5454 Ystone_wB, FALSE, TRUE,
5455 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5462 Xnut_pause, FALSE, FALSE,
5466 Xnut_fall, FALSE, FALSE,
5470 Ynut_s, FALSE, FALSE,
5471 EL_NUT, ACTION_FALLING, -1
5474 Ynut_sB, FALSE, TRUE,
5475 EL_NUT, ACTION_FALLING, -1
5478 Ynut_e, FALSE, FALSE,
5479 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5482 Ynut_eB, FALSE, TRUE,
5483 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5486 Ynut_w, FALSE, FALSE,
5487 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5490 Ynut_wB, FALSE, TRUE,
5491 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5494 Xbug_n, TRUE, FALSE,
5498 Xbug_e, TRUE, FALSE,
5499 EL_BUG_RIGHT, -1, -1
5502 Xbug_s, TRUE, FALSE,
5506 Xbug_w, TRUE, FALSE,
5510 Xbug_gon, FALSE, FALSE,
5514 Xbug_goe, FALSE, FALSE,
5515 EL_BUG_RIGHT, -1, -1
5518 Xbug_gos, FALSE, FALSE,
5522 Xbug_gow, FALSE, FALSE,
5526 Ybug_n, FALSE, FALSE,
5527 EL_BUG, ACTION_MOVING, MV_BIT_UP
5530 Ybug_nB, FALSE, TRUE,
5531 EL_BUG, ACTION_MOVING, MV_BIT_UP
5534 Ybug_e, FALSE, FALSE,
5535 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5538 Ybug_eB, FALSE, TRUE,
5539 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5542 Ybug_s, FALSE, FALSE,
5543 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5546 Ybug_sB, FALSE, TRUE,
5547 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5550 Ybug_w, FALSE, FALSE,
5551 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5554 Ybug_wB, FALSE, TRUE,
5555 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5558 Ybug_w_n, FALSE, FALSE,
5559 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5562 Ybug_n_e, FALSE, FALSE,
5563 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5566 Ybug_e_s, FALSE, FALSE,
5567 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5570 Ybug_s_w, FALSE, FALSE,
5571 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5574 Ybug_e_n, FALSE, FALSE,
5575 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5578 Ybug_s_e, FALSE, FALSE,
5579 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5582 Ybug_w_s, FALSE, FALSE,
5583 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5586 Ybug_n_w, FALSE, FALSE,
5587 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5590 Ybug_stone, FALSE, FALSE,
5591 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5594 Ybug_spring, FALSE, FALSE,
5595 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5598 Xtank_n, TRUE, FALSE,
5599 EL_SPACESHIP_UP, -1, -1
5602 Xtank_e, TRUE, FALSE,
5603 EL_SPACESHIP_RIGHT, -1, -1
5606 Xtank_s, TRUE, FALSE,
5607 EL_SPACESHIP_DOWN, -1, -1
5610 Xtank_w, TRUE, FALSE,
5611 EL_SPACESHIP_LEFT, -1, -1
5614 Xtank_gon, FALSE, FALSE,
5615 EL_SPACESHIP_UP, -1, -1
5618 Xtank_goe, FALSE, FALSE,
5619 EL_SPACESHIP_RIGHT, -1, -1
5622 Xtank_gos, FALSE, FALSE,
5623 EL_SPACESHIP_DOWN, -1, -1
5626 Xtank_gow, FALSE, FALSE,
5627 EL_SPACESHIP_LEFT, -1, -1
5630 Ytank_n, FALSE, FALSE,
5631 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5634 Ytank_nB, FALSE, TRUE,
5635 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5638 Ytank_e, FALSE, FALSE,
5639 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5642 Ytank_eB, FALSE, TRUE,
5643 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5646 Ytank_s, FALSE, FALSE,
5647 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5650 Ytank_sB, FALSE, TRUE,
5651 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5654 Ytank_w, FALSE, FALSE,
5655 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5658 Ytank_wB, FALSE, TRUE,
5659 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5662 Ytank_w_n, FALSE, FALSE,
5663 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5666 Ytank_n_e, FALSE, FALSE,
5667 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5670 Ytank_e_s, FALSE, FALSE,
5671 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5674 Ytank_s_w, FALSE, FALSE,
5675 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5678 Ytank_e_n, FALSE, FALSE,
5679 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5682 Ytank_s_e, FALSE, FALSE,
5683 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5686 Ytank_w_s, FALSE, FALSE,
5687 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5690 Ytank_n_w, FALSE, FALSE,
5691 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5694 Ytank_stone, FALSE, FALSE,
5695 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5698 Ytank_spring, FALSE, FALSE,
5699 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5702 Xandroid, TRUE, FALSE,
5703 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5706 Xandroid_1_n, FALSE, FALSE,
5707 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5710 Xandroid_2_n, FALSE, FALSE,
5711 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5714 Xandroid_1_e, FALSE, FALSE,
5715 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5718 Xandroid_2_e, FALSE, FALSE,
5719 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5722 Xandroid_1_w, FALSE, FALSE,
5723 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5726 Xandroid_2_w, FALSE, FALSE,
5727 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5730 Xandroid_1_s, FALSE, FALSE,
5731 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5734 Xandroid_2_s, FALSE, FALSE,
5735 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5738 Yandroid_n, FALSE, FALSE,
5739 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5742 Yandroid_nB, FALSE, TRUE,
5743 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5746 Yandroid_ne, FALSE, FALSE,
5747 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5750 Yandroid_neB, FALSE, TRUE,
5751 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5754 Yandroid_e, FALSE, FALSE,
5755 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5758 Yandroid_eB, FALSE, TRUE,
5759 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5762 Yandroid_se, FALSE, FALSE,
5763 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5766 Yandroid_seB, FALSE, TRUE,
5767 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5770 Yandroid_s, FALSE, FALSE,
5771 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5774 Yandroid_sB, FALSE, TRUE,
5775 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5778 Yandroid_sw, FALSE, FALSE,
5779 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5782 Yandroid_swB, FALSE, TRUE,
5783 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5786 Yandroid_w, FALSE, FALSE,
5787 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5790 Yandroid_wB, FALSE, TRUE,
5791 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5794 Yandroid_nw, FALSE, FALSE,
5795 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5798 Yandroid_nwB, FALSE, TRUE,
5799 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5802 Xspring, TRUE, FALSE,
5806 Xspring_pause, FALSE, FALSE,
5810 Xspring_e, FALSE, FALSE,
5814 Xspring_w, FALSE, FALSE,
5818 Xspring_fall, FALSE, FALSE,
5822 Yspring_s, FALSE, FALSE,
5823 EL_SPRING, ACTION_FALLING, -1
5826 Yspring_sB, FALSE, TRUE,
5827 EL_SPRING, ACTION_FALLING, -1
5830 Yspring_e, FALSE, FALSE,
5831 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5834 Yspring_eB, FALSE, TRUE,
5835 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5838 Yspring_w, FALSE, FALSE,
5839 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5842 Yspring_wB, FALSE, TRUE,
5843 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5846 Yspring_kill_e, FALSE, FALSE,
5847 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5850 Yspring_kill_eB, FALSE, TRUE,
5851 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5854 Yspring_kill_w, FALSE, FALSE,
5855 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5858 Yspring_kill_wB, FALSE, TRUE,
5859 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5862 Xeater_n, TRUE, FALSE,
5863 EL_YAMYAM_UP, -1, -1
5866 Xeater_e, TRUE, FALSE,
5867 EL_YAMYAM_RIGHT, -1, -1
5870 Xeater_w, TRUE, FALSE,
5871 EL_YAMYAM_LEFT, -1, -1
5874 Xeater_s, TRUE, FALSE,
5875 EL_YAMYAM_DOWN, -1, -1
5878 Yeater_n, FALSE, FALSE,
5879 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5882 Yeater_nB, FALSE, TRUE,
5883 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5886 Yeater_e, FALSE, FALSE,
5887 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5890 Yeater_eB, FALSE, TRUE,
5891 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5894 Yeater_s, FALSE, FALSE,
5895 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5898 Yeater_sB, FALSE, TRUE,
5899 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5902 Yeater_w, FALSE, FALSE,
5903 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5906 Yeater_wB, FALSE, TRUE,
5907 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5910 Yeater_stone, FALSE, FALSE,
5911 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5914 Yeater_spring, FALSE, FALSE,
5915 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5918 Xalien, TRUE, FALSE,
5922 Xalien_pause, FALSE, FALSE,
5926 Yalien_n, FALSE, FALSE,
5927 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5930 Yalien_nB, FALSE, TRUE,
5931 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5934 Yalien_e, FALSE, FALSE,
5935 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5938 Yalien_eB, FALSE, TRUE,
5939 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5942 Yalien_s, FALSE, FALSE,
5943 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5946 Yalien_sB, FALSE, TRUE,
5947 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5950 Yalien_w, FALSE, FALSE,
5951 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5954 Yalien_wB, FALSE, TRUE,
5955 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5958 Yalien_stone, FALSE, FALSE,
5959 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5962 Yalien_spring, FALSE, FALSE,
5963 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5966 Xemerald, TRUE, FALSE,
5970 Xemerald_pause, FALSE, FALSE,
5974 Xemerald_fall, FALSE, FALSE,
5978 Xemerald_shine, FALSE, FALSE,
5979 EL_EMERALD, ACTION_TWINKLING, -1
5982 Yemerald_s, FALSE, FALSE,
5983 EL_EMERALD, ACTION_FALLING, -1
5986 Yemerald_sB, FALSE, TRUE,
5987 EL_EMERALD, ACTION_FALLING, -1
5990 Yemerald_e, FALSE, FALSE,
5991 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5994 Yemerald_eB, FALSE, TRUE,
5995 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5998 Yemerald_w, FALSE, FALSE,
5999 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6002 Yemerald_wB, FALSE, TRUE,
6003 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6006 Yemerald_eat, FALSE, FALSE,
6007 EL_EMERALD, ACTION_COLLECTING, -1
6010 Yemerald_stone, FALSE, FALSE,
6011 EL_NUT, ACTION_BREAKING, -1
6014 Xdiamond, TRUE, FALSE,
6018 Xdiamond_pause, FALSE, FALSE,
6022 Xdiamond_fall, FALSE, FALSE,
6026 Xdiamond_shine, FALSE, FALSE,
6027 EL_DIAMOND, ACTION_TWINKLING, -1
6030 Ydiamond_s, FALSE, FALSE,
6031 EL_DIAMOND, ACTION_FALLING, -1
6034 Ydiamond_sB, FALSE, TRUE,
6035 EL_DIAMOND, ACTION_FALLING, -1
6038 Ydiamond_e, FALSE, FALSE,
6039 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6042 Ydiamond_eB, FALSE, TRUE,
6043 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6046 Ydiamond_w, FALSE, FALSE,
6047 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6050 Ydiamond_wB, FALSE, TRUE,
6051 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6054 Ydiamond_eat, FALSE, FALSE,
6055 EL_DIAMOND, ACTION_COLLECTING, -1
6058 Ydiamond_stone, FALSE, FALSE,
6059 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6062 Xdrip_fall, TRUE, FALSE,
6063 EL_AMOEBA_DROP, -1, -1
6066 Xdrip_stretch, FALSE, FALSE,
6067 EL_AMOEBA_DROP, ACTION_FALLING, -1
6070 Xdrip_stretchB, FALSE, TRUE,
6071 EL_AMOEBA_DROP, ACTION_FALLING, -1
6074 Xdrip_eat, FALSE, FALSE,
6075 EL_AMOEBA_DROP, ACTION_GROWING, -1
6078 Ydrip_s1, FALSE, FALSE,
6079 EL_AMOEBA_DROP, ACTION_FALLING, -1
6082 Ydrip_s1B, FALSE, TRUE,
6083 EL_AMOEBA_DROP, ACTION_FALLING, -1
6086 Ydrip_s2, FALSE, FALSE,
6087 EL_AMOEBA_DROP, ACTION_FALLING, -1
6090 Ydrip_s2B, FALSE, TRUE,
6091 EL_AMOEBA_DROP, ACTION_FALLING, -1
6098 Xbomb_pause, FALSE, FALSE,
6102 Xbomb_fall, FALSE, FALSE,
6106 Ybomb_s, FALSE, FALSE,
6107 EL_BOMB, ACTION_FALLING, -1
6110 Ybomb_sB, FALSE, TRUE,
6111 EL_BOMB, ACTION_FALLING, -1
6114 Ybomb_e, FALSE, FALSE,
6115 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6118 Ybomb_eB, FALSE, TRUE,
6119 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6122 Ybomb_w, FALSE, FALSE,
6123 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6126 Ybomb_wB, FALSE, TRUE,
6127 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6130 Ybomb_eat, FALSE, FALSE,
6131 EL_BOMB, ACTION_ACTIVATING, -1
6134 Xballoon, TRUE, FALSE,
6138 Yballoon_n, FALSE, FALSE,
6139 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6142 Yballoon_nB, FALSE, TRUE,
6143 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6146 Yballoon_e, FALSE, FALSE,
6147 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6150 Yballoon_eB, FALSE, TRUE,
6151 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6154 Yballoon_s, FALSE, FALSE,
6155 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6158 Yballoon_sB, FALSE, TRUE,
6159 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6162 Yballoon_w, FALSE, FALSE,
6163 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6166 Yballoon_wB, FALSE, TRUE,
6167 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6170 Xgrass, TRUE, FALSE,
6171 EL_EMC_GRASS, -1, -1
6174 Ygrass_nB, FALSE, FALSE,
6175 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6178 Ygrass_eB, FALSE, FALSE,
6179 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6182 Ygrass_sB, FALSE, FALSE,
6183 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6186 Ygrass_wB, FALSE, FALSE,
6187 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6194 Ydirt_nB, FALSE, FALSE,
6195 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6198 Ydirt_eB, FALSE, FALSE,
6199 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6202 Ydirt_sB, FALSE, FALSE,
6203 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6206 Ydirt_wB, FALSE, FALSE,
6207 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6210 Xacid_ne, TRUE, FALSE,
6211 EL_ACID_POOL_TOPRIGHT, -1, -1
6214 Xacid_se, TRUE, FALSE,
6215 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6218 Xacid_s, TRUE, FALSE,
6219 EL_ACID_POOL_BOTTOM, -1, -1
6222 Xacid_sw, TRUE, FALSE,
6223 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6226 Xacid_nw, TRUE, FALSE,
6227 EL_ACID_POOL_TOPLEFT, -1, -1
6230 Xacid_1, TRUE, FALSE,
6234 Xacid_2, FALSE, FALSE,
6238 Xacid_3, FALSE, FALSE,
6242 Xacid_4, FALSE, FALSE,
6246 Xacid_5, FALSE, FALSE,
6250 Xacid_6, FALSE, FALSE,
6254 Xacid_7, FALSE, FALSE,
6258 Xacid_8, FALSE, FALSE,
6262 Xball_1, TRUE, FALSE,
6263 EL_EMC_MAGIC_BALL, -1, -1
6266 Xball_1B, FALSE, FALSE,
6267 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6270 Xball_2, FALSE, FALSE,
6271 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6274 Xball_2B, FALSE, FALSE,
6275 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6278 Yball_eat, FALSE, FALSE,
6279 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6282 Ykey_1_eat, FALSE, FALSE,
6283 EL_EM_KEY_1, ACTION_COLLECTING, -1
6286 Ykey_2_eat, FALSE, FALSE,
6287 EL_EM_KEY_2, ACTION_COLLECTING, -1
6290 Ykey_3_eat, FALSE, FALSE,
6291 EL_EM_KEY_3, ACTION_COLLECTING, -1
6294 Ykey_4_eat, FALSE, FALSE,
6295 EL_EM_KEY_4, ACTION_COLLECTING, -1
6298 Ykey_5_eat, FALSE, FALSE,
6299 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6302 Ykey_6_eat, FALSE, FALSE,
6303 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6306 Ykey_7_eat, FALSE, FALSE,
6307 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6310 Ykey_8_eat, FALSE, FALSE,
6311 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6314 Ylenses_eat, FALSE, FALSE,
6315 EL_EMC_LENSES, ACTION_COLLECTING, -1
6318 Ymagnify_eat, FALSE, FALSE,
6319 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6322 Ygrass_eat, FALSE, FALSE,
6323 EL_EMC_GRASS, ACTION_SNAPPING, -1
6326 Ydirt_eat, FALSE, FALSE,
6327 EL_SAND, ACTION_SNAPPING, -1
6330 Xgrow_ns, TRUE, FALSE,
6331 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6334 Ygrow_ns_eat, FALSE, FALSE,
6335 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6338 Xgrow_ew, TRUE, FALSE,
6339 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6342 Ygrow_ew_eat, FALSE, FALSE,
6343 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6346 Xwonderwall, TRUE, FALSE,
6347 EL_MAGIC_WALL, -1, -1
6350 XwonderwallB, FALSE, FALSE,
6351 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6354 Xamoeba_1, TRUE, FALSE,
6355 EL_AMOEBA_DRY, ACTION_OTHER, -1
6358 Xamoeba_2, FALSE, FALSE,
6359 EL_AMOEBA_DRY, ACTION_OTHER, -1
6362 Xamoeba_3, FALSE, FALSE,
6363 EL_AMOEBA_DRY, ACTION_OTHER, -1
6366 Xamoeba_4, FALSE, FALSE,
6367 EL_AMOEBA_DRY, ACTION_OTHER, -1
6370 Xamoeba_5, TRUE, FALSE,
6371 EL_AMOEBA_WET, ACTION_OTHER, -1
6374 Xamoeba_6, FALSE, FALSE,
6375 EL_AMOEBA_WET, ACTION_OTHER, -1
6378 Xamoeba_7, FALSE, FALSE,
6379 EL_AMOEBA_WET, ACTION_OTHER, -1
6382 Xamoeba_8, FALSE, FALSE,
6383 EL_AMOEBA_WET, ACTION_OTHER, -1
6386 Xdoor_1, TRUE, FALSE,
6387 EL_EM_GATE_1, -1, -1
6390 Xdoor_2, TRUE, FALSE,
6391 EL_EM_GATE_2, -1, -1
6394 Xdoor_3, TRUE, FALSE,
6395 EL_EM_GATE_3, -1, -1
6398 Xdoor_4, TRUE, FALSE,
6399 EL_EM_GATE_4, -1, -1
6402 Xdoor_5, TRUE, FALSE,
6403 EL_EMC_GATE_5, -1, -1
6406 Xdoor_6, TRUE, FALSE,
6407 EL_EMC_GATE_6, -1, -1
6410 Xdoor_7, TRUE, FALSE,
6411 EL_EMC_GATE_7, -1, -1
6414 Xdoor_8, TRUE, FALSE,
6415 EL_EMC_GATE_8, -1, -1
6418 Xkey_1, TRUE, FALSE,
6422 Xkey_2, TRUE, FALSE,
6426 Xkey_3, TRUE, FALSE,
6430 Xkey_4, TRUE, FALSE,
6434 Xkey_5, TRUE, FALSE,
6435 EL_EMC_KEY_5, -1, -1
6438 Xkey_6, TRUE, FALSE,
6439 EL_EMC_KEY_6, -1, -1
6442 Xkey_7, TRUE, FALSE,
6443 EL_EMC_KEY_7, -1, -1
6446 Xkey_8, TRUE, FALSE,
6447 EL_EMC_KEY_8, -1, -1
6450 Xwind_n, TRUE, FALSE,
6451 EL_BALLOON_SWITCH_UP, -1, -1
6454 Xwind_e, TRUE, FALSE,
6455 EL_BALLOON_SWITCH_RIGHT, -1, -1
6458 Xwind_s, TRUE, FALSE,
6459 EL_BALLOON_SWITCH_DOWN, -1, -1
6462 Xwind_w, TRUE, FALSE,
6463 EL_BALLOON_SWITCH_LEFT, -1, -1
6466 Xwind_nesw, TRUE, FALSE,
6467 EL_BALLOON_SWITCH_ANY, -1, -1
6470 Xwind_stop, TRUE, FALSE,
6471 EL_BALLOON_SWITCH_NONE, -1, -1
6475 EL_EM_EXIT_CLOSED, -1, -1
6478 Xexit_1, TRUE, FALSE,
6479 EL_EM_EXIT_OPEN, -1, -1
6482 Xexit_2, FALSE, FALSE,
6483 EL_EM_EXIT_OPEN, -1, -1
6486 Xexit_3, FALSE, FALSE,
6487 EL_EM_EXIT_OPEN, -1, -1
6490 Xdynamite, TRUE, FALSE,
6491 EL_EM_DYNAMITE, -1, -1
6494 Ydynamite_eat, FALSE, FALSE,
6495 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6498 Xdynamite_1, TRUE, FALSE,
6499 EL_EM_DYNAMITE_ACTIVE, -1, -1
6502 Xdynamite_2, FALSE, FALSE,
6503 EL_EM_DYNAMITE_ACTIVE, -1, -1
6506 Xdynamite_3, FALSE, FALSE,
6507 EL_EM_DYNAMITE_ACTIVE, -1, -1
6510 Xdynamite_4, FALSE, FALSE,
6511 EL_EM_DYNAMITE_ACTIVE, -1, -1
6514 Xbumper, TRUE, FALSE,
6515 EL_EMC_SPRING_BUMPER, -1, -1
6518 XbumperB, FALSE, FALSE,
6519 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6522 Xwheel, TRUE, FALSE,
6523 EL_ROBOT_WHEEL, -1, -1
6526 XwheelB, FALSE, FALSE,
6527 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6530 Xswitch, TRUE, FALSE,
6531 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6534 XswitchB, FALSE, FALSE,
6535 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6539 EL_QUICKSAND_EMPTY, -1, -1
6542 Xsand_stone, TRUE, FALSE,
6543 EL_QUICKSAND_FULL, -1, -1
6546 Xsand_stonein_1, FALSE, TRUE,
6547 EL_ROCK, ACTION_FILLING, -1
6550 Xsand_stonein_2, FALSE, TRUE,
6551 EL_ROCK, ACTION_FILLING, -1
6554 Xsand_stonein_3, FALSE, TRUE,
6555 EL_ROCK, ACTION_FILLING, -1
6558 Xsand_stonein_4, FALSE, TRUE,
6559 EL_ROCK, ACTION_FILLING, -1
6562 Xsand_stonesand_1, FALSE, FALSE,
6563 EL_QUICKSAND_EMPTYING, -1, -1
6566 Xsand_stonesand_2, FALSE, FALSE,
6567 EL_QUICKSAND_EMPTYING, -1, -1
6570 Xsand_stonesand_3, FALSE, FALSE,
6571 EL_QUICKSAND_EMPTYING, -1, -1
6574 Xsand_stonesand_4, FALSE, FALSE,
6575 EL_QUICKSAND_EMPTYING, -1, -1
6578 Xsand_stonesand_quickout_1, FALSE, FALSE,
6579 EL_QUICKSAND_EMPTYING, -1, -1
6582 Xsand_stonesand_quickout_2, FALSE, FALSE,
6583 EL_QUICKSAND_EMPTYING, -1, -1
6586 Xsand_stoneout_1, FALSE, FALSE,
6587 EL_ROCK, ACTION_EMPTYING, -1
6590 Xsand_stoneout_2, FALSE, FALSE,
6591 EL_ROCK, ACTION_EMPTYING, -1
6594 Xsand_sandstone_1, FALSE, FALSE,
6595 EL_QUICKSAND_FILLING, -1, -1
6598 Xsand_sandstone_2, FALSE, FALSE,
6599 EL_QUICKSAND_FILLING, -1, -1
6602 Xsand_sandstone_3, FALSE, FALSE,
6603 EL_QUICKSAND_FILLING, -1, -1
6606 Xsand_sandstone_4, FALSE, FALSE,
6607 EL_QUICKSAND_FILLING, -1, -1
6610 Xplant, TRUE, FALSE,
6611 EL_EMC_PLANT, -1, -1
6614 Yplant, FALSE, FALSE,
6615 EL_EMC_PLANT, -1, -1
6618 Xlenses, TRUE, FALSE,
6619 EL_EMC_LENSES, -1, -1
6622 Xmagnify, TRUE, FALSE,
6623 EL_EMC_MAGNIFIER, -1, -1
6626 Xdripper, TRUE, FALSE,
6627 EL_EMC_DRIPPER, -1, -1
6630 XdripperB, FALSE, FALSE,
6631 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6634 Xfake_blank, TRUE, FALSE,
6635 EL_INVISIBLE_WALL, -1, -1
6638 Xfake_blankB, FALSE, FALSE,
6639 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6642 Xfake_grass, TRUE, FALSE,
6643 EL_EMC_FAKE_GRASS, -1, -1
6646 Xfake_grassB, FALSE, FALSE,
6647 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6650 Xfake_door_1, TRUE, FALSE,
6651 EL_EM_GATE_1_GRAY, -1, -1
6654 Xfake_door_2, TRUE, FALSE,
6655 EL_EM_GATE_2_GRAY, -1, -1
6658 Xfake_door_3, TRUE, FALSE,
6659 EL_EM_GATE_3_GRAY, -1, -1
6662 Xfake_door_4, TRUE, FALSE,
6663 EL_EM_GATE_4_GRAY, -1, -1
6666 Xfake_door_5, TRUE, FALSE,
6667 EL_EMC_GATE_5_GRAY, -1, -1
6670 Xfake_door_6, TRUE, FALSE,
6671 EL_EMC_GATE_6_GRAY, -1, -1
6674 Xfake_door_7, TRUE, FALSE,
6675 EL_EMC_GATE_7_GRAY, -1, -1
6678 Xfake_door_8, TRUE, FALSE,
6679 EL_EMC_GATE_8_GRAY, -1, -1
6682 Xfake_acid_1, TRUE, FALSE,
6683 EL_EMC_FAKE_ACID, -1, -1
6686 Xfake_acid_2, FALSE, FALSE,
6687 EL_EMC_FAKE_ACID, -1, -1
6690 Xfake_acid_3, FALSE, FALSE,
6691 EL_EMC_FAKE_ACID, -1, -1
6694 Xfake_acid_4, FALSE, FALSE,
6695 EL_EMC_FAKE_ACID, -1, -1
6698 Xfake_acid_5, FALSE, FALSE,
6699 EL_EMC_FAKE_ACID, -1, -1
6702 Xfake_acid_6, FALSE, FALSE,
6703 EL_EMC_FAKE_ACID, -1, -1
6706 Xfake_acid_7, FALSE, FALSE,
6707 EL_EMC_FAKE_ACID, -1, -1
6710 Xfake_acid_8, FALSE, FALSE,
6711 EL_EMC_FAKE_ACID, -1, -1
6714 Xsteel_1, TRUE, FALSE,
6715 EL_STEELWALL, -1, -1
6718 Xsteel_2, TRUE, FALSE,
6719 EL_EMC_STEELWALL_2, -1, -1
6722 Xsteel_3, TRUE, FALSE,
6723 EL_EMC_STEELWALL_3, -1, -1
6726 Xsteel_4, TRUE, FALSE,
6727 EL_EMC_STEELWALL_4, -1, -1
6730 Xwall_1, TRUE, FALSE,
6734 Xwall_2, TRUE, FALSE,
6735 EL_EMC_WALL_14, -1, -1
6738 Xwall_3, TRUE, FALSE,
6739 EL_EMC_WALL_15, -1, -1
6742 Xwall_4, TRUE, FALSE,
6743 EL_EMC_WALL_16, -1, -1
6746 Xround_wall_1, TRUE, FALSE,
6747 EL_WALL_SLIPPERY, -1, -1
6750 Xround_wall_2, TRUE, FALSE,
6751 EL_EMC_WALL_SLIPPERY_2, -1, -1
6754 Xround_wall_3, TRUE, FALSE,
6755 EL_EMC_WALL_SLIPPERY_3, -1, -1
6758 Xround_wall_4, TRUE, FALSE,
6759 EL_EMC_WALL_SLIPPERY_4, -1, -1
6762 Xdecor_1, TRUE, FALSE,
6763 EL_EMC_WALL_8, -1, -1
6766 Xdecor_2, TRUE, FALSE,
6767 EL_EMC_WALL_6, -1, -1
6770 Xdecor_3, TRUE, FALSE,
6771 EL_EMC_WALL_4, -1, -1
6774 Xdecor_4, TRUE, FALSE,
6775 EL_EMC_WALL_7, -1, -1
6778 Xdecor_5, TRUE, FALSE,
6779 EL_EMC_WALL_5, -1, -1
6782 Xdecor_6, TRUE, FALSE,
6783 EL_EMC_WALL_9, -1, -1
6786 Xdecor_7, TRUE, FALSE,
6787 EL_EMC_WALL_10, -1, -1
6790 Xdecor_8, TRUE, FALSE,
6791 EL_EMC_WALL_1, -1, -1
6794 Xdecor_9, TRUE, FALSE,
6795 EL_EMC_WALL_2, -1, -1
6798 Xdecor_10, TRUE, FALSE,
6799 EL_EMC_WALL_3, -1, -1
6802 Xdecor_11, TRUE, FALSE,
6803 EL_EMC_WALL_11, -1, -1
6806 Xdecor_12, TRUE, FALSE,
6807 EL_EMC_WALL_12, -1, -1
6810 Xalpha_0, TRUE, FALSE,
6811 EL_CHAR('0'), -1, -1
6814 Xalpha_1, TRUE, FALSE,
6815 EL_CHAR('1'), -1, -1
6818 Xalpha_2, TRUE, FALSE,
6819 EL_CHAR('2'), -1, -1
6822 Xalpha_3, TRUE, FALSE,
6823 EL_CHAR('3'), -1, -1
6826 Xalpha_4, TRUE, FALSE,
6827 EL_CHAR('4'), -1, -1
6830 Xalpha_5, TRUE, FALSE,
6831 EL_CHAR('5'), -1, -1
6834 Xalpha_6, TRUE, FALSE,
6835 EL_CHAR('6'), -1, -1
6838 Xalpha_7, TRUE, FALSE,
6839 EL_CHAR('7'), -1, -1
6842 Xalpha_8, TRUE, FALSE,
6843 EL_CHAR('8'), -1, -1
6846 Xalpha_9, TRUE, FALSE,
6847 EL_CHAR('9'), -1, -1
6850 Xalpha_excla, TRUE, FALSE,
6851 EL_CHAR('!'), -1, -1
6854 Xalpha_quote, TRUE, FALSE,
6855 EL_CHAR('"'), -1, -1
6858 Xalpha_comma, TRUE, FALSE,
6859 EL_CHAR(','), -1, -1
6862 Xalpha_minus, TRUE, FALSE,
6863 EL_CHAR('-'), -1, -1
6866 Xalpha_perio, TRUE, FALSE,
6867 EL_CHAR('.'), -1, -1
6870 Xalpha_colon, TRUE, FALSE,
6871 EL_CHAR(':'), -1, -1
6874 Xalpha_quest, TRUE, FALSE,
6875 EL_CHAR('?'), -1, -1
6878 Xalpha_a, TRUE, FALSE,
6879 EL_CHAR('A'), -1, -1
6882 Xalpha_b, TRUE, FALSE,
6883 EL_CHAR('B'), -1, -1
6886 Xalpha_c, TRUE, FALSE,
6887 EL_CHAR('C'), -1, -1
6890 Xalpha_d, TRUE, FALSE,
6891 EL_CHAR('D'), -1, -1
6894 Xalpha_e, TRUE, FALSE,
6895 EL_CHAR('E'), -1, -1
6898 Xalpha_f, TRUE, FALSE,
6899 EL_CHAR('F'), -1, -1
6902 Xalpha_g, TRUE, FALSE,
6903 EL_CHAR('G'), -1, -1
6906 Xalpha_h, TRUE, FALSE,
6907 EL_CHAR('H'), -1, -1
6910 Xalpha_i, TRUE, FALSE,
6911 EL_CHAR('I'), -1, -1
6914 Xalpha_j, TRUE, FALSE,
6915 EL_CHAR('J'), -1, -1
6918 Xalpha_k, TRUE, FALSE,
6919 EL_CHAR('K'), -1, -1
6922 Xalpha_l, TRUE, FALSE,
6923 EL_CHAR('L'), -1, -1
6926 Xalpha_m, TRUE, FALSE,
6927 EL_CHAR('M'), -1, -1
6930 Xalpha_n, TRUE, FALSE,
6931 EL_CHAR('N'), -1, -1
6934 Xalpha_o, TRUE, FALSE,
6935 EL_CHAR('O'), -1, -1
6938 Xalpha_p, TRUE, FALSE,
6939 EL_CHAR('P'), -1, -1
6942 Xalpha_q, TRUE, FALSE,
6943 EL_CHAR('Q'), -1, -1
6946 Xalpha_r, TRUE, FALSE,
6947 EL_CHAR('R'), -1, -1
6950 Xalpha_s, TRUE, FALSE,
6951 EL_CHAR('S'), -1, -1
6954 Xalpha_t, TRUE, FALSE,
6955 EL_CHAR('T'), -1, -1
6958 Xalpha_u, TRUE, FALSE,
6959 EL_CHAR('U'), -1, -1
6962 Xalpha_v, TRUE, FALSE,
6963 EL_CHAR('V'), -1, -1
6966 Xalpha_w, TRUE, FALSE,
6967 EL_CHAR('W'), -1, -1
6970 Xalpha_x, TRUE, FALSE,
6971 EL_CHAR('X'), -1, -1
6974 Xalpha_y, TRUE, FALSE,
6975 EL_CHAR('Y'), -1, -1
6978 Xalpha_z, TRUE, FALSE,
6979 EL_CHAR('Z'), -1, -1
6982 Xalpha_arrow_e, TRUE, FALSE,
6983 EL_CHAR('>'), -1, -1
6986 Xalpha_arrow_w, TRUE, FALSE,
6987 EL_CHAR('<'), -1, -1
6990 Xalpha_copyr, TRUE, FALSE,
6991 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6995 Xboom_bug, FALSE, FALSE,
6996 EL_BUG, ACTION_EXPLODING, -1
6999 Xboom_bomb, FALSE, FALSE,
7000 EL_BOMB, ACTION_EXPLODING, -1
7003 Xboom_android, FALSE, FALSE,
7004 EL_EMC_ANDROID, ACTION_OTHER, -1
7007 Xboom_1, FALSE, FALSE,
7008 EL_DEFAULT, ACTION_EXPLODING, -1
7011 Xboom_2, FALSE, FALSE,
7012 EL_DEFAULT, ACTION_EXPLODING, -1
7015 Znormal, FALSE, FALSE,
7019 Zdynamite, FALSE, FALSE,
7023 Zplayer, FALSE, FALSE,
7027 ZBORDER, FALSE, FALSE,
7037 static struct Mapping_EM_to_RND_player
7046 em_player_mapping_list[] =
7050 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7054 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7058 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7062 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7066 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7070 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7074 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7078 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7082 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7086 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7090 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7094 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7098 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7102 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7106 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7110 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7114 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7118 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7122 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7126 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7130 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7134 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7138 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7142 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7146 EL_PLAYER_1, ACTION_DEFAULT, -1,
7150 EL_PLAYER_2, ACTION_DEFAULT, -1,
7154 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7158 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7162 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7166 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7170 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7174 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7178 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7182 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7186 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7190 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7194 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7198 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7202 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7206 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7210 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7214 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7218 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7222 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7226 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7230 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7234 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7238 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7242 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7246 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7250 EL_PLAYER_3, ACTION_DEFAULT, -1,
7254 EL_PLAYER_4, ACTION_DEFAULT, -1,
7263 int map_element_RND_to_EM(int element_rnd)
7265 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7266 static boolean mapping_initialized = FALSE;
7268 if (!mapping_initialized)
7272 /* return "Xalpha_quest" for all undefined elements in mapping array */
7273 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7274 mapping_RND_to_EM[i] = Xalpha_quest;
7276 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7277 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7278 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7279 em_object_mapping_list[i].element_em;
7281 mapping_initialized = TRUE;
7284 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7285 return mapping_RND_to_EM[element_rnd];
7287 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7292 int map_element_EM_to_RND(int element_em)
7294 static unsigned short mapping_EM_to_RND[TILE_MAX];
7295 static boolean mapping_initialized = FALSE;
7297 if (!mapping_initialized)
7301 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7302 for (i = 0; i < TILE_MAX; i++)
7303 mapping_EM_to_RND[i] = EL_UNKNOWN;
7305 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7306 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7307 em_object_mapping_list[i].element_rnd;
7309 mapping_initialized = TRUE;
7312 if (element_em >= 0 && element_em < TILE_MAX)
7313 return mapping_EM_to_RND[element_em];
7315 Error(ERR_WARN, "invalid EM level element %d", element_em);
7320 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7322 struct LevelInfo_EM *level_em = level->native_em_level;
7323 struct LEVEL *lev = level_em->lev;
7326 for (i = 0; i < TILE_MAX; i++)
7327 lev->android_array[i] = Xblank;
7329 for (i = 0; i < level->num_android_clone_elements; i++)
7331 int element_rnd = level->android_clone_element[i];
7332 int element_em = map_element_RND_to_EM(element_rnd);
7334 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7335 if (em_object_mapping_list[j].element_rnd == element_rnd)
7336 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7340 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7342 struct LevelInfo_EM *level_em = level->native_em_level;
7343 struct LEVEL *lev = level_em->lev;
7346 level->num_android_clone_elements = 0;
7348 for (i = 0; i < TILE_MAX; i++)
7350 int element_em = lev->android_array[i];
7352 boolean element_found = FALSE;
7354 if (element_em == Xblank)
7357 element_rnd = map_element_EM_to_RND(element_em);
7359 for (j = 0; j < level->num_android_clone_elements; j++)
7360 if (level->android_clone_element[j] == element_rnd)
7361 element_found = TRUE;
7365 level->android_clone_element[level->num_android_clone_elements++] =
7368 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7373 if (level->num_android_clone_elements == 0)
7375 level->num_android_clone_elements = 1;
7376 level->android_clone_element[0] = EL_EMPTY;
7380 int map_direction_RND_to_EM(int direction)
7382 return (direction == MV_UP ? 0 :
7383 direction == MV_RIGHT ? 1 :
7384 direction == MV_DOWN ? 2 :
7385 direction == MV_LEFT ? 3 :
7389 int map_direction_EM_to_RND(int direction)
7391 return (direction == 0 ? MV_UP :
7392 direction == 1 ? MV_RIGHT :
7393 direction == 2 ? MV_DOWN :
7394 direction == 3 ? MV_LEFT :
7398 int map_element_RND_to_SP(int element_rnd)
7400 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7402 if (element_rnd >= EL_SP_START &&
7403 element_rnd <= EL_SP_END)
7404 element_sp = element_rnd - EL_SP_START;
7405 else if (element_rnd == EL_EMPTY_SPACE)
7407 else if (element_rnd == EL_INVISIBLE_WALL)
7413 int map_element_SP_to_RND(int element_sp)
7415 int element_rnd = EL_UNKNOWN;
7417 if (element_sp >= 0x00 &&
7419 element_rnd = EL_SP_START + element_sp;
7420 else if (element_sp == 0x28)
7421 element_rnd = EL_INVISIBLE_WALL;
7426 int map_action_SP_to_RND(int action_sp)
7430 case actActive: return ACTION_ACTIVE;
7431 case actImpact: return ACTION_IMPACT;
7432 case actExploding: return ACTION_EXPLODING;
7433 case actDigging: return ACTION_DIGGING;
7434 case actSnapping: return ACTION_SNAPPING;
7435 case actCollecting: return ACTION_COLLECTING;
7436 case actPassing: return ACTION_PASSING;
7437 case actPushing: return ACTION_PUSHING;
7438 case actDropping: return ACTION_DROPPING;
7440 default: return ACTION_DEFAULT;
7444 int map_element_RND_to_MM(int element_rnd)
7446 return (element_rnd >= EL_MM_START_1 &&
7447 element_rnd <= EL_MM_END_1 ?
7448 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7450 element_rnd >= EL_MM_START_2 &&
7451 element_rnd <= EL_MM_END_2 ?
7452 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7454 element_rnd >= EL_CHAR_START &&
7455 element_rnd <= EL_CHAR_END ?
7456 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7458 element_rnd >= EL_MM_RUNTIME_START &&
7459 element_rnd <= EL_MM_RUNTIME_END ?
7460 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7462 element_rnd >= EL_MM_DUMMY_START &&
7463 element_rnd <= EL_MM_DUMMY_END ?
7464 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7466 EL_MM_EMPTY_NATIVE);
7469 int map_element_MM_to_RND(int element_mm)
7471 return (element_mm == EL_MM_EMPTY_NATIVE ||
7472 element_mm == EL_DF_EMPTY_NATIVE ?
7475 element_mm >= EL_MM_START_1_NATIVE &&
7476 element_mm <= EL_MM_END_1_NATIVE ?
7477 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7479 element_mm >= EL_MM_START_2_NATIVE &&
7480 element_mm <= EL_MM_END_2_NATIVE ?
7481 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7483 element_mm >= EL_MM_CHAR_START_NATIVE &&
7484 element_mm <= EL_MM_CHAR_END_NATIVE ?
7485 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7487 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7488 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7489 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7491 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7492 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7493 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7498 int map_action_MM_to_RND(int action_mm)
7500 /* all MM actions are defined to exactly match their RND counterparts */
7504 int map_sound_MM_to_RND(int sound_mm)
7508 case SND_MM_GAME_LEVELTIME_CHARGING:
7509 return SND_GAME_LEVELTIME_CHARGING;
7511 case SND_MM_GAME_HEALTH_CHARGING:
7512 return SND_GAME_HEALTH_CHARGING;
7515 return SND_UNDEFINED;
7519 int map_mm_wall_element(int element)
7521 return (element >= EL_MM_STEEL_WALL_START &&
7522 element <= EL_MM_STEEL_WALL_END ?
7525 element >= EL_MM_WOODEN_WALL_START &&
7526 element <= EL_MM_WOODEN_WALL_END ?
7529 element >= EL_MM_ICE_WALL_START &&
7530 element <= EL_MM_ICE_WALL_END ?
7533 element >= EL_MM_AMOEBA_WALL_START &&
7534 element <= EL_MM_AMOEBA_WALL_END ?
7537 element >= EL_DF_STEEL_WALL_START &&
7538 element <= EL_DF_STEEL_WALL_END ?
7541 element >= EL_DF_WOODEN_WALL_START &&
7542 element <= EL_DF_WOODEN_WALL_END ?
7548 int map_mm_wall_element_editor(int element)
7552 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7553 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7554 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7555 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7556 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7557 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7559 default: return element;
7563 int get_next_element(int element)
7567 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7568 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7569 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7570 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7571 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7572 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7573 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7574 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7575 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7576 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7577 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7579 default: return element;
7583 int el2img_mm(int element_mm)
7585 return el2img(map_element_MM_to_RND(element_mm));
7588 int el_act_dir2img(int element, int action, int direction)
7590 element = GFX_ELEMENT(element);
7591 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7593 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7594 return element_info[element].direction_graphic[action][direction];
7597 static int el_act_dir2crm(int element, int action, int direction)
7599 element = GFX_ELEMENT(element);
7600 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7602 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7603 return element_info[element].direction_crumbled[action][direction];
7606 int el_act2img(int element, int action)
7608 element = GFX_ELEMENT(element);
7610 return element_info[element].graphic[action];
7613 int el_act2crm(int element, int action)
7615 element = GFX_ELEMENT(element);
7617 return element_info[element].crumbled[action];
7620 int el_dir2img(int element, int direction)
7622 element = GFX_ELEMENT(element);
7624 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7627 int el2baseimg(int element)
7629 return element_info[element].graphic[ACTION_DEFAULT];
7632 int el2img(int element)
7634 element = GFX_ELEMENT(element);
7636 return element_info[element].graphic[ACTION_DEFAULT];
7639 int el2edimg(int element)
7641 element = GFX_ELEMENT(element);
7643 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7646 int el2preimg(int element)
7648 element = GFX_ELEMENT(element);
7650 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7653 int el2panelimg(int element)
7655 element = GFX_ELEMENT(element);
7657 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7660 int font2baseimg(int font_nr)
7662 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7665 int getBeltNrFromBeltElement(int element)
7667 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7668 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7669 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7672 int getBeltNrFromBeltActiveElement(int element)
7674 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7675 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7676 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7679 int getBeltNrFromBeltSwitchElement(int element)
7681 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7682 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7683 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7686 int getBeltDirNrFromBeltElement(int element)
7688 static int belt_base_element[4] =
7690 EL_CONVEYOR_BELT_1_LEFT,
7691 EL_CONVEYOR_BELT_2_LEFT,
7692 EL_CONVEYOR_BELT_3_LEFT,
7693 EL_CONVEYOR_BELT_4_LEFT
7696 int belt_nr = getBeltNrFromBeltElement(element);
7697 int belt_dir_nr = element - belt_base_element[belt_nr];
7699 return (belt_dir_nr % 3);
7702 int getBeltDirNrFromBeltSwitchElement(int element)
7704 static int belt_base_element[4] =
7706 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7707 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7708 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7709 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7712 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7713 int belt_dir_nr = element - belt_base_element[belt_nr];
7715 return (belt_dir_nr % 3);
7718 int getBeltDirFromBeltElement(int element)
7720 static int belt_move_dir[3] =
7727 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7729 return belt_move_dir[belt_dir_nr];
7732 int getBeltDirFromBeltSwitchElement(int element)
7734 static int belt_move_dir[3] =
7741 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7743 return belt_move_dir[belt_dir_nr];
7746 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7748 static int belt_base_element[4] =
7750 EL_CONVEYOR_BELT_1_LEFT,
7751 EL_CONVEYOR_BELT_2_LEFT,
7752 EL_CONVEYOR_BELT_3_LEFT,
7753 EL_CONVEYOR_BELT_4_LEFT
7756 return belt_base_element[belt_nr] + belt_dir_nr;
7759 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7761 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7763 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7766 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7768 static int belt_base_element[4] =
7770 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7771 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7772 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7773 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7776 return belt_base_element[belt_nr] + belt_dir_nr;
7779 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7781 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7783 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7786 boolean getTeamMode_EM()
7788 return game.team_mode;
7791 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7793 int game_frame_delay_value;
7795 game_frame_delay_value =
7796 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7797 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7800 if (tape.playing && tape.warp_forward && !tape.pausing)
7801 game_frame_delay_value = 0;
7803 return game_frame_delay_value;
7806 unsigned int InitRND(int seed)
7808 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7809 return InitEngineRandom_EM(seed);
7810 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7811 return InitEngineRandom_SP(seed);
7812 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7813 return InitEngineRandom_MM(seed);
7815 return InitEngineRandom_RND(seed);
7818 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7819 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7821 inline static int get_effective_element_EM(int tile, int frame_em)
7823 int element = object_mapping[tile].element_rnd;
7824 int action = object_mapping[tile].action;
7825 boolean is_backside = object_mapping[tile].is_backside;
7826 boolean action_removing = (action == ACTION_DIGGING ||
7827 action == ACTION_SNAPPING ||
7828 action == ACTION_COLLECTING);
7834 case Yacid_splash_eB:
7835 case Yacid_splash_wB:
7836 return (frame_em > 5 ? EL_EMPTY : element);
7842 else /* frame_em == 7 */
7846 case Yacid_splash_eB:
7847 case Yacid_splash_wB:
7850 case Yemerald_stone:
7853 case Ydiamond_stone:
7857 case Xdrip_stretchB:
7876 case Xsand_stonein_1:
7877 case Xsand_stonein_2:
7878 case Xsand_stonein_3:
7879 case Xsand_stonein_4:
7883 return (is_backside || action_removing ? EL_EMPTY : element);
7888 inline static boolean check_linear_animation_EM(int tile)
7892 case Xsand_stonesand_1:
7893 case Xsand_stonesand_quickout_1:
7894 case Xsand_sandstone_1:
7895 case Xsand_stonein_1:
7896 case Xsand_stoneout_1:
7915 case Yacid_splash_eB:
7916 case Yacid_splash_wB:
7917 case Yemerald_stone:
7924 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7925 boolean has_crumbled_graphics,
7926 int crumbled, int sync_frame)
7928 /* if element can be crumbled, but certain action graphics are just empty
7929 space (like instantly snapping sand to empty space in 1 frame), do not
7930 treat these empty space graphics as crumbled graphics in EMC engine */
7931 if (crumbled == IMG_EMPTY_SPACE)
7932 has_crumbled_graphics = FALSE;
7934 if (has_crumbled_graphics)
7936 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7937 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7938 g_crumbled->anim_delay,
7939 g_crumbled->anim_mode,
7940 g_crumbled->anim_start_frame,
7943 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7944 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7946 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7947 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7949 g_em->has_crumbled_graphics = TRUE;
7953 g_em->crumbled_bitmap = NULL;
7954 g_em->crumbled_src_x = 0;
7955 g_em->crumbled_src_y = 0;
7956 g_em->crumbled_border_size = 0;
7957 g_em->crumbled_tile_size = 0;
7959 g_em->has_crumbled_graphics = FALSE;
7963 void ResetGfxAnimation_EM(int x, int y, int tile)
7968 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7969 int tile, int frame_em, int x, int y)
7971 int action = object_mapping[tile].action;
7972 int direction = object_mapping[tile].direction;
7973 int effective_element = get_effective_element_EM(tile, frame_em);
7974 int graphic = (direction == MV_NONE ?
7975 el_act2img(effective_element, action) :
7976 el_act_dir2img(effective_element, action, direction));
7977 struct GraphicInfo *g = &graphic_info[graphic];
7979 boolean action_removing = (action == ACTION_DIGGING ||
7980 action == ACTION_SNAPPING ||
7981 action == ACTION_COLLECTING);
7982 boolean action_moving = (action == ACTION_FALLING ||
7983 action == ACTION_MOVING ||
7984 action == ACTION_PUSHING ||
7985 action == ACTION_EATING ||
7986 action == ACTION_FILLING ||
7987 action == ACTION_EMPTYING);
7988 boolean action_falling = (action == ACTION_FALLING ||
7989 action == ACTION_FILLING ||
7990 action == ACTION_EMPTYING);
7992 /* special case: graphic uses "2nd movement tile" and has defined
7993 7 frames for movement animation (or less) => use default graphic
7994 for last (8th) frame which ends the movement animation */
7995 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7997 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7998 graphic = (direction == MV_NONE ?
7999 el_act2img(effective_element, action) :
8000 el_act_dir2img(effective_element, action, direction));
8002 g = &graphic_info[graphic];
8005 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8009 else if (action_moving)
8011 boolean is_backside = object_mapping[tile].is_backside;
8015 int direction = object_mapping[tile].direction;
8016 int move_dir = (action_falling ? MV_DOWN : direction);
8021 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8022 if (g->double_movement && frame_em == 0)
8026 if (move_dir == MV_LEFT)
8027 GfxFrame[x - 1][y] = GfxFrame[x][y];
8028 else if (move_dir == MV_RIGHT)
8029 GfxFrame[x + 1][y] = GfxFrame[x][y];
8030 else if (move_dir == MV_UP)
8031 GfxFrame[x][y - 1] = GfxFrame[x][y];
8032 else if (move_dir == MV_DOWN)
8033 GfxFrame[x][y + 1] = GfxFrame[x][y];
8040 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8041 if (tile == Xsand_stonesand_quickout_1 ||
8042 tile == Xsand_stonesand_quickout_2)
8046 if (graphic_info[graphic].anim_global_sync)
8047 sync_frame = FrameCounter;
8048 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8049 sync_frame = GfxFrame[x][y];
8051 sync_frame = 0; /* playfield border (pseudo steel) */
8053 SetRandomAnimationValue(x, y);
8055 int frame = getAnimationFrame(g->anim_frames,
8058 g->anim_start_frame,
8061 g_em->unique_identifier =
8062 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8065 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8066 int tile, int frame_em, int x, int y)
8068 int action = object_mapping[tile].action;
8069 int direction = object_mapping[tile].direction;
8070 boolean is_backside = object_mapping[tile].is_backside;
8071 int effective_element = get_effective_element_EM(tile, frame_em);
8072 int effective_action = action;
8073 int graphic = (direction == MV_NONE ?
8074 el_act2img(effective_element, effective_action) :
8075 el_act_dir2img(effective_element, effective_action,
8077 int crumbled = (direction == MV_NONE ?
8078 el_act2crm(effective_element, effective_action) :
8079 el_act_dir2crm(effective_element, effective_action,
8081 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8082 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8083 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8084 struct GraphicInfo *g = &graphic_info[graphic];
8087 /* special case: graphic uses "2nd movement tile" and has defined
8088 7 frames for movement animation (or less) => use default graphic
8089 for last (8th) frame which ends the movement animation */
8090 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8092 effective_action = ACTION_DEFAULT;
8093 graphic = (direction == MV_NONE ?
8094 el_act2img(effective_element, effective_action) :
8095 el_act_dir2img(effective_element, effective_action,
8097 crumbled = (direction == MV_NONE ?
8098 el_act2crm(effective_element, effective_action) :
8099 el_act_dir2crm(effective_element, effective_action,
8102 g = &graphic_info[graphic];
8105 if (graphic_info[graphic].anim_global_sync)
8106 sync_frame = FrameCounter;
8107 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8108 sync_frame = GfxFrame[x][y];
8110 sync_frame = 0; /* playfield border (pseudo steel) */
8112 SetRandomAnimationValue(x, y);
8114 int frame = getAnimationFrame(g->anim_frames,
8117 g->anim_start_frame,
8120 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8121 g->double_movement && is_backside);
8123 /* (updating the "crumbled" graphic definitions is probably not really needed,
8124 as animations for crumbled graphics can't be longer than one EMC cycle) */
8125 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8129 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8130 int player_nr, int anim, int frame_em)
8132 int element = player_mapping[player_nr][anim].element_rnd;
8133 int action = player_mapping[player_nr][anim].action;
8134 int direction = player_mapping[player_nr][anim].direction;
8135 int graphic = (direction == MV_NONE ?
8136 el_act2img(element, action) :
8137 el_act_dir2img(element, action, direction));
8138 struct GraphicInfo *g = &graphic_info[graphic];
8141 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8143 stored_player[player_nr].StepFrame = frame_em;
8145 sync_frame = stored_player[player_nr].Frame;
8147 int frame = getAnimationFrame(g->anim_frames,
8150 g->anim_start_frame,
8153 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8154 &g_em->src_x, &g_em->src_y, FALSE);
8157 void InitGraphicInfo_EM(void)
8162 int num_em_gfx_errors = 0;
8164 if (graphic_info_em_object[0][0].bitmap == NULL)
8166 /* EM graphics not yet initialized in em_open_all() */
8171 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8174 /* always start with reliable default values */
8175 for (i = 0; i < TILE_MAX; i++)
8177 object_mapping[i].element_rnd = EL_UNKNOWN;
8178 object_mapping[i].is_backside = FALSE;
8179 object_mapping[i].action = ACTION_DEFAULT;
8180 object_mapping[i].direction = MV_NONE;
8183 /* always start with reliable default values */
8184 for (p = 0; p < MAX_PLAYERS; p++)
8186 for (i = 0; i < SPR_MAX; i++)
8188 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8189 player_mapping[p][i].action = ACTION_DEFAULT;
8190 player_mapping[p][i].direction = MV_NONE;
8194 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8196 int e = em_object_mapping_list[i].element_em;
8198 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8199 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8201 if (em_object_mapping_list[i].action != -1)
8202 object_mapping[e].action = em_object_mapping_list[i].action;
8204 if (em_object_mapping_list[i].direction != -1)
8205 object_mapping[e].direction =
8206 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8209 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8211 int a = em_player_mapping_list[i].action_em;
8212 int p = em_player_mapping_list[i].player_nr;
8214 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8216 if (em_player_mapping_list[i].action != -1)
8217 player_mapping[p][a].action = em_player_mapping_list[i].action;
8219 if (em_player_mapping_list[i].direction != -1)
8220 player_mapping[p][a].direction =
8221 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8224 for (i = 0; i < TILE_MAX; i++)
8226 int element = object_mapping[i].element_rnd;
8227 int action = object_mapping[i].action;
8228 int direction = object_mapping[i].direction;
8229 boolean is_backside = object_mapping[i].is_backside;
8230 boolean action_exploding = ((action == ACTION_EXPLODING ||
8231 action == ACTION_SMASHED_BY_ROCK ||
8232 action == ACTION_SMASHED_BY_SPRING) &&
8233 element != EL_DIAMOND);
8234 boolean action_active = (action == ACTION_ACTIVE);
8235 boolean action_other = (action == ACTION_OTHER);
8237 for (j = 0; j < 8; j++)
8239 int effective_element = get_effective_element_EM(i, j);
8240 int effective_action = (j < 7 ? action :
8241 i == Xdrip_stretch ? action :
8242 i == Xdrip_stretchB ? action :
8243 i == Ydrip_s1 ? action :
8244 i == Ydrip_s1B ? action :
8245 i == Xball_1B ? action :
8246 i == Xball_2 ? action :
8247 i == Xball_2B ? action :
8248 i == Yball_eat ? action :
8249 i == Ykey_1_eat ? action :
8250 i == Ykey_2_eat ? action :
8251 i == Ykey_3_eat ? action :
8252 i == Ykey_4_eat ? action :
8253 i == Ykey_5_eat ? action :
8254 i == Ykey_6_eat ? action :
8255 i == Ykey_7_eat ? action :
8256 i == Ykey_8_eat ? action :
8257 i == Ylenses_eat ? action :
8258 i == Ymagnify_eat ? action :
8259 i == Ygrass_eat ? action :
8260 i == Ydirt_eat ? action :
8261 i == Xsand_stonein_1 ? action :
8262 i == Xsand_stonein_2 ? action :
8263 i == Xsand_stonein_3 ? action :
8264 i == Xsand_stonein_4 ? action :
8265 i == Xsand_stoneout_1 ? action :
8266 i == Xsand_stoneout_2 ? action :
8267 i == Xboom_android ? ACTION_EXPLODING :
8268 action_exploding ? ACTION_EXPLODING :
8269 action_active ? action :
8270 action_other ? action :
8272 int graphic = (el_act_dir2img(effective_element, effective_action,
8274 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8276 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8277 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8278 boolean has_action_graphics = (graphic != base_graphic);
8279 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8280 struct GraphicInfo *g = &graphic_info[graphic];
8281 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8284 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8285 boolean special_animation = (action != ACTION_DEFAULT &&
8286 g->anim_frames == 3 &&
8287 g->anim_delay == 2 &&
8288 g->anim_mode & ANIM_LINEAR);
8289 int sync_frame = (i == Xdrip_stretch ? 7 :
8290 i == Xdrip_stretchB ? 7 :
8291 i == Ydrip_s2 ? j + 8 :
8292 i == Ydrip_s2B ? j + 8 :
8301 i == Xfake_acid_1 ? 0 :
8302 i == Xfake_acid_2 ? 10 :
8303 i == Xfake_acid_3 ? 20 :
8304 i == Xfake_acid_4 ? 30 :
8305 i == Xfake_acid_5 ? 40 :
8306 i == Xfake_acid_6 ? 50 :
8307 i == Xfake_acid_7 ? 60 :
8308 i == Xfake_acid_8 ? 70 :
8310 i == Xball_2B ? j + 8 :
8311 i == Yball_eat ? j + 1 :
8312 i == Ykey_1_eat ? j + 1 :
8313 i == Ykey_2_eat ? j + 1 :
8314 i == Ykey_3_eat ? j + 1 :
8315 i == Ykey_4_eat ? j + 1 :
8316 i == Ykey_5_eat ? j + 1 :
8317 i == Ykey_6_eat ? j + 1 :
8318 i == Ykey_7_eat ? j + 1 :
8319 i == Ykey_8_eat ? j + 1 :
8320 i == Ylenses_eat ? j + 1 :
8321 i == Ymagnify_eat ? j + 1 :
8322 i == Ygrass_eat ? j + 1 :
8323 i == Ydirt_eat ? j + 1 :
8324 i == Xamoeba_1 ? 0 :
8325 i == Xamoeba_2 ? 1 :
8326 i == Xamoeba_3 ? 2 :
8327 i == Xamoeba_4 ? 3 :
8328 i == Xamoeba_5 ? 0 :
8329 i == Xamoeba_6 ? 1 :
8330 i == Xamoeba_7 ? 2 :
8331 i == Xamoeba_8 ? 3 :
8332 i == Xexit_2 ? j + 8 :
8333 i == Xexit_3 ? j + 16 :
8334 i == Xdynamite_1 ? 0 :
8335 i == Xdynamite_2 ? 8 :
8336 i == Xdynamite_3 ? 16 :
8337 i == Xdynamite_4 ? 24 :
8338 i == Xsand_stonein_1 ? j + 1 :
8339 i == Xsand_stonein_2 ? j + 9 :
8340 i == Xsand_stonein_3 ? j + 17 :
8341 i == Xsand_stonein_4 ? j + 25 :
8342 i == Xsand_stoneout_1 && j == 0 ? 0 :
8343 i == Xsand_stoneout_1 && j == 1 ? 0 :
8344 i == Xsand_stoneout_1 && j == 2 ? 1 :
8345 i == Xsand_stoneout_1 && j == 3 ? 2 :
8346 i == Xsand_stoneout_1 && j == 4 ? 2 :
8347 i == Xsand_stoneout_1 && j == 5 ? 3 :
8348 i == Xsand_stoneout_1 && j == 6 ? 4 :
8349 i == Xsand_stoneout_1 && j == 7 ? 4 :
8350 i == Xsand_stoneout_2 && j == 0 ? 5 :
8351 i == Xsand_stoneout_2 && j == 1 ? 6 :
8352 i == Xsand_stoneout_2 && j == 2 ? 7 :
8353 i == Xsand_stoneout_2 && j == 3 ? 8 :
8354 i == Xsand_stoneout_2 && j == 4 ? 9 :
8355 i == Xsand_stoneout_2 && j == 5 ? 11 :
8356 i == Xsand_stoneout_2 && j == 6 ? 13 :
8357 i == Xsand_stoneout_2 && j == 7 ? 15 :
8358 i == Xboom_bug && j == 1 ? 2 :
8359 i == Xboom_bug && j == 2 ? 2 :
8360 i == Xboom_bug && j == 3 ? 4 :
8361 i == Xboom_bug && j == 4 ? 4 :
8362 i == Xboom_bug && j == 5 ? 2 :
8363 i == Xboom_bug && j == 6 ? 2 :
8364 i == Xboom_bug && j == 7 ? 0 :
8365 i == Xboom_bomb && j == 1 ? 2 :
8366 i == Xboom_bomb && j == 2 ? 2 :
8367 i == Xboom_bomb && j == 3 ? 4 :
8368 i == Xboom_bomb && j == 4 ? 4 :
8369 i == Xboom_bomb && j == 5 ? 2 :
8370 i == Xboom_bomb && j == 6 ? 2 :
8371 i == Xboom_bomb && j == 7 ? 0 :
8372 i == Xboom_android && j == 7 ? 6 :
8373 i == Xboom_1 && j == 1 ? 2 :
8374 i == Xboom_1 && j == 2 ? 2 :
8375 i == Xboom_1 && j == 3 ? 4 :
8376 i == Xboom_1 && j == 4 ? 4 :
8377 i == Xboom_1 && j == 5 ? 6 :
8378 i == Xboom_1 && j == 6 ? 6 :
8379 i == Xboom_1 && j == 7 ? 8 :
8380 i == Xboom_2 && j == 0 ? 8 :
8381 i == Xboom_2 && j == 1 ? 8 :
8382 i == Xboom_2 && j == 2 ? 10 :
8383 i == Xboom_2 && j == 3 ? 10 :
8384 i == Xboom_2 && j == 4 ? 10 :
8385 i == Xboom_2 && j == 5 ? 12 :
8386 i == Xboom_2 && j == 6 ? 12 :
8387 i == Xboom_2 && j == 7 ? 12 :
8388 special_animation && j == 4 ? 3 :
8389 effective_action != action ? 0 :
8393 Bitmap *debug_bitmap = g_em->bitmap;
8394 int debug_src_x = g_em->src_x;
8395 int debug_src_y = g_em->src_y;
8398 int frame = getAnimationFrame(g->anim_frames,
8401 g->anim_start_frame,
8404 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8405 g->double_movement && is_backside);
8407 g_em->bitmap = src_bitmap;
8408 g_em->src_x = src_x;
8409 g_em->src_y = src_y;
8410 g_em->src_offset_x = 0;
8411 g_em->src_offset_y = 0;
8412 g_em->dst_offset_x = 0;
8413 g_em->dst_offset_y = 0;
8414 g_em->width = TILEX;
8415 g_em->height = TILEY;
8417 g_em->preserve_background = FALSE;
8419 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8422 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8423 effective_action == ACTION_MOVING ||
8424 effective_action == ACTION_PUSHING ||
8425 effective_action == ACTION_EATING)) ||
8426 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8427 effective_action == ACTION_EMPTYING)))
8430 (effective_action == ACTION_FALLING ||
8431 effective_action == ACTION_FILLING ||
8432 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8433 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8434 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8435 int num_steps = (i == Ydrip_s1 ? 16 :
8436 i == Ydrip_s1B ? 16 :
8437 i == Ydrip_s2 ? 16 :
8438 i == Ydrip_s2B ? 16 :
8439 i == Xsand_stonein_1 ? 32 :
8440 i == Xsand_stonein_2 ? 32 :
8441 i == Xsand_stonein_3 ? 32 :
8442 i == Xsand_stonein_4 ? 32 :
8443 i == Xsand_stoneout_1 ? 16 :
8444 i == Xsand_stoneout_2 ? 16 : 8);
8445 int cx = ABS(dx) * (TILEX / num_steps);
8446 int cy = ABS(dy) * (TILEY / num_steps);
8447 int step_frame = (i == Ydrip_s2 ? j + 8 :
8448 i == Ydrip_s2B ? j + 8 :
8449 i == Xsand_stonein_2 ? j + 8 :
8450 i == Xsand_stonein_3 ? j + 16 :
8451 i == Xsand_stonein_4 ? j + 24 :
8452 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8453 int step = (is_backside ? step_frame : num_steps - step_frame);
8455 if (is_backside) /* tile where movement starts */
8457 if (dx < 0 || dy < 0)
8459 g_em->src_offset_x = cx * step;
8460 g_em->src_offset_y = cy * step;
8464 g_em->dst_offset_x = cx * step;
8465 g_em->dst_offset_y = cy * step;
8468 else /* tile where movement ends */
8470 if (dx < 0 || dy < 0)
8472 g_em->dst_offset_x = cx * step;
8473 g_em->dst_offset_y = cy * step;
8477 g_em->src_offset_x = cx * step;
8478 g_em->src_offset_y = cy * step;
8482 g_em->width = TILEX - cx * step;
8483 g_em->height = TILEY - cy * step;
8486 /* create unique graphic identifier to decide if tile must be redrawn */
8487 /* bit 31 - 16 (16 bit): EM style graphic
8488 bit 15 - 12 ( 4 bit): EM style frame
8489 bit 11 - 6 ( 6 bit): graphic width
8490 bit 5 - 0 ( 6 bit): graphic height */
8491 g_em->unique_identifier =
8492 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8496 /* skip check for EMC elements not contained in original EMC artwork */
8497 if (element == EL_EMC_FAKE_ACID)
8500 if (g_em->bitmap != debug_bitmap ||
8501 g_em->src_x != debug_src_x ||
8502 g_em->src_y != debug_src_y ||
8503 g_em->src_offset_x != 0 ||
8504 g_em->src_offset_y != 0 ||
8505 g_em->dst_offset_x != 0 ||
8506 g_em->dst_offset_y != 0 ||
8507 g_em->width != TILEX ||
8508 g_em->height != TILEY)
8510 static int last_i = -1;
8518 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8519 i, element, element_info[element].token_name,
8520 element_action_info[effective_action].suffix, direction);
8522 if (element != effective_element)
8523 printf(" [%d ('%s')]",
8525 element_info[effective_element].token_name);
8529 if (g_em->bitmap != debug_bitmap)
8530 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8531 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8533 if (g_em->src_x != debug_src_x ||
8534 g_em->src_y != debug_src_y)
8535 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8536 j, (is_backside ? 'B' : 'F'),
8537 g_em->src_x, g_em->src_y,
8538 g_em->src_x / 32, g_em->src_y / 32,
8539 debug_src_x, debug_src_y,
8540 debug_src_x / 32, debug_src_y / 32);
8542 if (g_em->src_offset_x != 0 ||
8543 g_em->src_offset_y != 0 ||
8544 g_em->dst_offset_x != 0 ||
8545 g_em->dst_offset_y != 0)
8546 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8548 g_em->src_offset_x, g_em->src_offset_y,
8549 g_em->dst_offset_x, g_em->dst_offset_y);
8551 if (g_em->width != TILEX ||
8552 g_em->height != TILEY)
8553 printf(" %d (%d): size %d,%d should be %d,%d\n",
8555 g_em->width, g_em->height, TILEX, TILEY);
8557 num_em_gfx_errors++;
8564 for (i = 0; i < TILE_MAX; i++)
8566 for (j = 0; j < 8; j++)
8568 int element = object_mapping[i].element_rnd;
8569 int action = object_mapping[i].action;
8570 int direction = object_mapping[i].direction;
8571 boolean is_backside = object_mapping[i].is_backside;
8572 int graphic_action = el_act_dir2img(element, action, direction);
8573 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8575 if ((action == ACTION_SMASHED_BY_ROCK ||
8576 action == ACTION_SMASHED_BY_SPRING ||
8577 action == ACTION_EATING) &&
8578 graphic_action == graphic_default)
8580 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8581 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8582 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8583 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8586 /* no separate animation for "smashed by rock" -- use rock instead */
8587 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8588 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8590 g_em->bitmap = g_xx->bitmap;
8591 g_em->src_x = g_xx->src_x;
8592 g_em->src_y = g_xx->src_y;
8593 g_em->src_offset_x = g_xx->src_offset_x;
8594 g_em->src_offset_y = g_xx->src_offset_y;
8595 g_em->dst_offset_x = g_xx->dst_offset_x;
8596 g_em->dst_offset_y = g_xx->dst_offset_y;
8597 g_em->width = g_xx->width;
8598 g_em->height = g_xx->height;
8599 g_em->unique_identifier = g_xx->unique_identifier;
8602 g_em->preserve_background = TRUE;
8607 for (p = 0; p < MAX_PLAYERS; p++)
8609 for (i = 0; i < SPR_MAX; i++)
8611 int element = player_mapping[p][i].element_rnd;
8612 int action = player_mapping[p][i].action;
8613 int direction = player_mapping[p][i].direction;
8615 for (j = 0; j < 8; j++)
8617 int effective_element = element;
8618 int effective_action = action;
8619 int graphic = (direction == MV_NONE ?
8620 el_act2img(effective_element, effective_action) :
8621 el_act_dir2img(effective_element, effective_action,
8623 struct GraphicInfo *g = &graphic_info[graphic];
8624 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8630 Bitmap *debug_bitmap = g_em->bitmap;
8631 int debug_src_x = g_em->src_x;
8632 int debug_src_y = g_em->src_y;
8635 int frame = getAnimationFrame(g->anim_frames,
8638 g->anim_start_frame,
8641 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8643 g_em->bitmap = src_bitmap;
8644 g_em->src_x = src_x;
8645 g_em->src_y = src_y;
8646 g_em->src_offset_x = 0;
8647 g_em->src_offset_y = 0;
8648 g_em->dst_offset_x = 0;
8649 g_em->dst_offset_y = 0;
8650 g_em->width = TILEX;
8651 g_em->height = TILEY;
8655 /* skip check for EMC elements not contained in original EMC artwork */
8656 if (element == EL_PLAYER_3 ||
8657 element == EL_PLAYER_4)
8660 if (g_em->bitmap != debug_bitmap ||
8661 g_em->src_x != debug_src_x ||
8662 g_em->src_y != debug_src_y)
8664 static int last_i = -1;
8672 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8673 p, i, element, element_info[element].token_name,
8674 element_action_info[effective_action].suffix, direction);
8676 if (element != effective_element)
8677 printf(" [%d ('%s')]",
8679 element_info[effective_element].token_name);
8683 if (g_em->bitmap != debug_bitmap)
8684 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8685 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8687 if (g_em->src_x != debug_src_x ||
8688 g_em->src_y != debug_src_y)
8689 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8691 g_em->src_x, g_em->src_y,
8692 g_em->src_x / 32, g_em->src_y / 32,
8693 debug_src_x, debug_src_y,
8694 debug_src_x / 32, debug_src_y / 32);
8696 num_em_gfx_errors++;
8706 printf("::: [%d errors found]\n", num_em_gfx_errors);
8712 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8713 boolean any_player_moving,
8714 boolean any_player_snapping,
8715 boolean any_player_dropping)
8717 if (frame == 0 && !any_player_dropping)
8719 if (!local_player->was_waiting)
8721 if (!CheckSaveEngineSnapshotToList())
8724 local_player->was_waiting = TRUE;
8727 else if (any_player_moving || any_player_snapping || any_player_dropping)
8729 local_player->was_waiting = FALSE;
8733 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8734 boolean murphy_is_dropping)
8736 if (murphy_is_waiting)
8738 if (!local_player->was_waiting)
8740 if (!CheckSaveEngineSnapshotToList())
8743 local_player->was_waiting = TRUE;
8748 local_player->was_waiting = FALSE;
8752 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
8753 boolean button_released)
8755 if (button_released)
8757 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
8758 CheckSaveEngineSnapshotToList();
8760 else if (element_clicked)
8762 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
8763 CheckSaveEngineSnapshotToList();
8765 game.snapshot.changed_action = TRUE;
8769 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8770 boolean any_player_moving,
8771 boolean any_player_snapping,
8772 boolean any_player_dropping)
8774 if (tape.single_step && tape.recording && !tape.pausing)
8775 if (frame == 0 && !any_player_dropping)
8776 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8778 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8779 any_player_snapping, any_player_dropping);
8782 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8783 boolean murphy_is_dropping)
8785 boolean murphy_starts_dropping = FALSE;
8788 for (i = 0; i < MAX_PLAYERS; i++)
8789 if (stored_player[i].force_dropping)
8790 murphy_starts_dropping = TRUE;
8792 if (tape.single_step && tape.recording && !tape.pausing)
8793 if (murphy_is_waiting && !murphy_starts_dropping)
8794 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8796 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8799 void CheckSingleStepMode_MM(boolean element_clicked,
8800 boolean button_released)
8802 if (tape.single_step && tape.recording && !tape.pausing)
8803 if (button_released)
8804 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8806 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
8809 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8810 int graphic, int sync_frame, int x, int y)
8812 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8814 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8817 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8819 return (IS_NEXT_FRAME(sync_frame, graphic));
8822 int getGraphicInfo_Delay(int graphic)
8824 return graphic_info[graphic].anim_delay;
8827 void PlayMenuSoundExt(int sound)
8829 if (sound == SND_UNDEFINED)
8832 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8833 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8836 if (IS_LOOP_SOUND(sound))
8837 PlaySoundLoop(sound);
8842 void PlayMenuSound()
8844 PlayMenuSoundExt(menu.sound[game_status]);
8847 void PlayMenuSoundStereo(int sound, int stereo_position)
8849 if (sound == SND_UNDEFINED)
8852 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8853 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8856 if (IS_LOOP_SOUND(sound))
8857 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8859 PlaySoundStereo(sound, stereo_position);
8862 void PlayMenuSoundIfLoopExt(int sound)
8864 if (sound == SND_UNDEFINED)
8867 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8868 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8871 if (IS_LOOP_SOUND(sound))
8872 PlaySoundLoop(sound);
8875 void PlayMenuSoundIfLoop()
8877 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8880 void PlayMenuMusicExt(int music)
8882 if (music == MUS_UNDEFINED)
8885 if (!setup.sound_music)
8891 void PlayMenuMusic()
8893 char *curr_music = getCurrentlyPlayingMusicFilename();
8894 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8896 if (!strEqual(curr_music, next_music))
8897 PlayMenuMusicExt(menu.music[game_status]);
8900 void PlayMenuSoundsAndMusic()
8906 static void FadeMenuSounds()
8911 static void FadeMenuMusic()
8913 char *curr_music = getCurrentlyPlayingMusicFilename();
8914 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8916 if (!strEqual(curr_music, next_music))
8920 void FadeMenuSoundsAndMusic()
8926 void PlaySoundActivating()
8929 PlaySound(SND_MENU_ITEM_ACTIVATING);
8933 void PlaySoundSelecting()
8936 PlaySound(SND_MENU_ITEM_SELECTING);
8940 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8942 boolean change_fullscreen = (setup.fullscreen !=
8943 video.fullscreen_enabled);
8944 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8945 setup.window_scaling_percent !=
8946 video.window_scaling_percent);
8948 if (change_window_scaling_percent && video.fullscreen_enabled)
8951 if (!change_window_scaling_percent && !video.fullscreen_available)
8954 #if defined(TARGET_SDL2)
8955 if (change_window_scaling_percent)
8957 SDLSetWindowScaling(setup.window_scaling_percent);
8961 else if (change_fullscreen)
8963 SDLSetWindowFullscreen(setup.fullscreen);
8965 /* set setup value according to successfully changed fullscreen mode */
8966 setup.fullscreen = video.fullscreen_enabled;
8972 if (change_fullscreen ||
8973 change_window_scaling_percent)
8975 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8977 /* save backbuffer content which gets lost when toggling fullscreen mode */
8978 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8980 if (change_window_scaling_percent)
8982 /* keep window mode, but change window scaling */
8983 video.fullscreen_enabled = TRUE; /* force new window scaling */
8986 /* toggle fullscreen */
8987 ChangeVideoModeIfNeeded(setup.fullscreen);
8989 /* set setup value according to successfully changed fullscreen mode */
8990 setup.fullscreen = video.fullscreen_enabled;
8992 /* restore backbuffer content from temporary backbuffer backup bitmap */
8993 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8995 FreeBitmap(tmp_backbuffer);
8997 /* update visible window/screen */
8998 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9002 void JoinRectangles(int *x, int *y, int *width, int *height,
9003 int x2, int y2, int width2, int height2)
9005 // do not join with "off-screen" rectangle
9006 if (x2 == -1 || y2 == -1)
9011 *width = MAX(*width, width2);
9012 *height = MAX(*height, height2);
9015 void SetAnimStatus(int anim_status_new)
9017 if (anim_status_new == GAME_MODE_MAIN)
9018 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9019 else if (anim_status_new == GAME_MODE_SCORES)
9020 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9022 global.anim_status_next = anim_status_new;
9024 // directly set screen modes that are entered without fading
9025 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9026 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9027 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9028 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9029 global.anim_status = global.anim_status_next;
9032 void SetGameStatus(int game_status_new)
9034 if (game_status_new != game_status)
9035 game_status_last_screen = game_status;
9037 game_status = game_status_new;
9039 SetAnimStatus(game_status_new);
9042 void SetFontStatus(int game_status_new)
9044 static int last_game_status = -1;
9046 if (game_status_new != -1)
9048 // set game status for font use after storing last game status
9049 last_game_status = game_status;
9050 game_status = game_status_new;
9054 // reset game status after font use from last stored game status
9055 game_status = last_game_status;
9059 void ResetFontStatus()
9064 boolean CheckIfPlayfieldViewportHasChanged()
9066 // if game status has not changed, playfield viewport has not changed either
9067 if (game_status == game_status_last)
9070 // check if playfield viewport has changed with current game status
9071 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9072 int new_real_sx = vp_playfield->x;
9073 int new_real_sy = vp_playfield->y;
9074 int new_full_sxsize = vp_playfield->width;
9075 int new_full_sysize = vp_playfield->height;
9077 return (new_real_sx != REAL_SX ||
9078 new_real_sy != REAL_SY ||
9079 new_full_sxsize != FULL_SXSIZE ||
9080 new_full_sysize != FULL_SYSIZE);
9083 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9085 return (CheckIfGlobalBorderHasChanged() ||
9086 CheckIfPlayfieldViewportHasChanged());
9089 void ChangeViewportPropertiesIfNeeded()
9091 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9092 FALSE : setup.small_game_graphics);
9093 int gfx_game_mode = game_status;
9094 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9096 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9097 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9098 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9099 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9100 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9101 int new_win_xsize = vp_window->width;
9102 int new_win_ysize = vp_window->height;
9103 int border_size = vp_playfield->border_size;
9104 int new_sx = vp_playfield->x + border_size;
9105 int new_sy = vp_playfield->y + border_size;
9106 int new_sxsize = vp_playfield->width - 2 * border_size;
9107 int new_sysize = vp_playfield->height - 2 * border_size;
9108 int new_real_sx = vp_playfield->x;
9109 int new_real_sy = vp_playfield->y;
9110 int new_full_sxsize = vp_playfield->width;
9111 int new_full_sysize = vp_playfield->height;
9112 int new_dx = vp_door_1->x;
9113 int new_dy = vp_door_1->y;
9114 int new_dxsize = vp_door_1->width;
9115 int new_dysize = vp_door_1->height;
9116 int new_vx = vp_door_2->x;
9117 int new_vy = vp_door_2->y;
9118 int new_vxsize = vp_door_2->width;
9119 int new_vysize = vp_door_2->height;
9120 int new_ex = vp_door_3->x;
9121 int new_ey = vp_door_3->y;
9122 int new_exsize = vp_door_3->width;
9123 int new_eysize = vp_door_3->height;
9124 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9125 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9126 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9127 int new_scr_fieldx = new_sxsize / tilesize;
9128 int new_scr_fieldy = new_sysize / tilesize;
9129 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9130 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9131 boolean init_gfx_buffers = FALSE;
9132 boolean init_video_buffer = FALSE;
9133 boolean init_gadgets_and_anims = FALSE;
9134 boolean init_em_graphics = FALSE;
9136 if (new_win_xsize != WIN_XSIZE ||
9137 new_win_ysize != WIN_YSIZE)
9139 WIN_XSIZE = new_win_xsize;
9140 WIN_YSIZE = new_win_ysize;
9142 init_video_buffer = TRUE;
9143 init_gfx_buffers = TRUE;
9144 init_gadgets_and_anims = TRUE;
9146 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9149 if (new_scr_fieldx != SCR_FIELDX ||
9150 new_scr_fieldy != SCR_FIELDY)
9152 /* this always toggles between MAIN and GAME when using small tile size */
9154 SCR_FIELDX = new_scr_fieldx;
9155 SCR_FIELDY = new_scr_fieldy;
9157 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9168 new_sxsize != SXSIZE ||
9169 new_sysize != SYSIZE ||
9170 new_dxsize != DXSIZE ||
9171 new_dysize != DYSIZE ||
9172 new_vxsize != VXSIZE ||
9173 new_vysize != VYSIZE ||
9174 new_exsize != EXSIZE ||
9175 new_eysize != EYSIZE ||
9176 new_real_sx != REAL_SX ||
9177 new_real_sy != REAL_SY ||
9178 new_full_sxsize != FULL_SXSIZE ||
9179 new_full_sysize != FULL_SYSIZE ||
9180 new_tilesize_var != TILESIZE_VAR
9183 // ------------------------------------------------------------------------
9184 // determine next fading area for changed viewport definitions
9185 // ------------------------------------------------------------------------
9187 // start with current playfield area (default fading area)
9190 FADE_SXSIZE = FULL_SXSIZE;
9191 FADE_SYSIZE = FULL_SYSIZE;
9193 // add new playfield area if position or size has changed
9194 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9195 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9197 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9198 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9201 // add current and new door 1 area if position or size has changed
9202 if (new_dx != DX || new_dy != DY ||
9203 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9205 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9206 DX, DY, DXSIZE, DYSIZE);
9207 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9208 new_dx, new_dy, new_dxsize, new_dysize);
9211 // add current and new door 2 area if position or size has changed
9212 if (new_dx != VX || new_dy != VY ||
9213 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9215 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9216 VX, VY, VXSIZE, VYSIZE);
9217 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9218 new_vx, new_vy, new_vxsize, new_vysize);
9221 // ------------------------------------------------------------------------
9222 // handle changed tile size
9223 // ------------------------------------------------------------------------
9225 if (new_tilesize_var != TILESIZE_VAR)
9227 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9229 // changing tile size invalidates scroll values of engine snapshots
9230 FreeEngineSnapshotSingle();
9232 // changing tile size requires update of graphic mapping for EM engine
9233 init_em_graphics = TRUE;
9244 SXSIZE = new_sxsize;
9245 SYSIZE = new_sysize;
9246 DXSIZE = new_dxsize;
9247 DYSIZE = new_dysize;
9248 VXSIZE = new_vxsize;
9249 VYSIZE = new_vysize;
9250 EXSIZE = new_exsize;
9251 EYSIZE = new_eysize;
9252 REAL_SX = new_real_sx;
9253 REAL_SY = new_real_sy;
9254 FULL_SXSIZE = new_full_sxsize;
9255 FULL_SYSIZE = new_full_sysize;
9256 TILESIZE_VAR = new_tilesize_var;
9258 init_gfx_buffers = TRUE;
9259 init_gadgets_and_anims = TRUE;
9261 // printf("::: viewports: init_gfx_buffers\n");
9262 // printf("::: viewports: init_gadgets_and_anims\n");
9265 if (init_gfx_buffers)
9267 // printf("::: init_gfx_buffers\n");
9269 SCR_FIELDX = new_scr_fieldx_buffers;
9270 SCR_FIELDY = new_scr_fieldy_buffers;
9274 SCR_FIELDX = new_scr_fieldx;
9275 SCR_FIELDY = new_scr_fieldy;
9277 SetDrawDeactivationMask(REDRAW_NONE);
9278 SetDrawBackgroundMask(REDRAW_FIELD);
9281 if (init_video_buffer)
9283 // printf("::: init_video_buffer\n");
9285 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9286 InitImageTextures();
9289 if (init_gadgets_and_anims)
9291 // printf("::: init_gadgets_and_anims\n");
9294 InitGlobalAnimations();
9297 if (init_em_graphics)
9299 InitGraphicInfo_EM();