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 int getLevelFromScreenX(int x)
363 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
364 return getLevelFromScreenX_EM(x);
365 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
366 return getLevelFromScreenX_SP(x);
368 return getLevelFromScreenX_RND(x);
371 int getLevelFromScreenY(int y)
373 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
374 return getLevelFromScreenY_EM(y);
375 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
376 return getLevelFromScreenY_SP(y);
378 return getLevelFromScreenY_RND(y);
381 void DumpTile(int x, int y)
386 printf_line("-", 79);
387 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
388 printf_line("-", 79);
390 if (!IN_LEV_FIELD(x, y))
392 printf("(not in level field)\n");
398 printf(" Feld: %d\t['%s']\n", Feld[x][y],
399 element_info[Feld[x][y]].token_name);
400 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
401 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
402 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
403 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
404 printf(" MovPos: %d\n", MovPos[x][y]);
405 printf(" MovDir: %d\n", MovDir[x][y]);
406 printf(" MovDelay: %d\n", MovDelay[x][y]);
407 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
408 printf(" CustomValue: %d\n", CustomValue[x][y]);
409 printf(" GfxElement: %d\n", GfxElement[x][y]);
410 printf(" GfxAction: %d\n", GfxAction[x][y]);
411 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
412 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
416 void DumpTileFromScreen(int sx, int sy)
418 int lx = getLevelFromScreenX(sx);
419 int ly = getLevelFromScreenY(sy);
424 void SetDrawtoField(int mode)
426 if (mode == DRAW_TO_FIELDBUFFER)
432 BX2 = SCR_FIELDX + 1;
433 BY2 = SCR_FIELDY + 1;
435 drawto_field = fieldbuffer;
437 else /* DRAW_TO_BACKBUFFER */
443 BX2 = SCR_FIELDX - 1;
444 BY2 = SCR_FIELDY - 1;
446 drawto_field = backbuffer;
450 static void RedrawPlayfield_RND()
452 if (game.envelope_active)
455 DrawLevel(REDRAW_ALL);
459 void RedrawPlayfield()
461 if (game_status != GAME_MODE_PLAYING)
464 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
465 RedrawPlayfield_EM(TRUE);
466 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
467 RedrawPlayfield_SP(TRUE);
468 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
469 RedrawPlayfield_RND();
471 BlitScreenToBitmap(backbuffer);
473 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
477 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
480 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
481 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
483 if (x == -1 && y == -1)
486 if (draw_target == DRAW_TO_SCREEN)
487 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
489 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
492 static void DrawMaskedBorderExt_FIELD(int draw_target)
494 if (global.border_status >= GAME_MODE_MAIN &&
495 global.border_status <= GAME_MODE_PLAYING &&
496 border.draw_masked[global.border_status])
497 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
501 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
503 // when drawing to backbuffer, never draw border over open doors
504 if (draw_target == DRAW_TO_BACKBUFFER &&
505 (GetDoorState() & DOOR_OPEN_1))
508 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
509 (global.border_status != GAME_MODE_EDITOR ||
510 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
511 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
514 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
516 // when drawing to backbuffer, never draw border over open doors
517 if (draw_target == DRAW_TO_BACKBUFFER &&
518 (GetDoorState() & DOOR_OPEN_2))
521 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
522 global.border_status != GAME_MODE_EDITOR)
523 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
526 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
528 /* currently not available */
531 static void DrawMaskedBorderExt_ALL(int draw_target)
533 DrawMaskedBorderExt_FIELD(draw_target);
534 DrawMaskedBorderExt_DOOR_1(draw_target);
535 DrawMaskedBorderExt_DOOR_2(draw_target);
536 DrawMaskedBorderExt_DOOR_3(draw_target);
539 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
541 /* never draw masked screen borders on borderless screens */
542 if (global.border_status == GAME_MODE_LOADING ||
543 global.border_status == GAME_MODE_TITLE)
546 if (redraw_mask & REDRAW_ALL)
547 DrawMaskedBorderExt_ALL(draw_target);
550 if (redraw_mask & REDRAW_FIELD)
551 DrawMaskedBorderExt_FIELD(draw_target);
552 if (redraw_mask & REDRAW_DOOR_1)
553 DrawMaskedBorderExt_DOOR_1(draw_target);
554 if (redraw_mask & REDRAW_DOOR_2)
555 DrawMaskedBorderExt_DOOR_2(draw_target);
556 if (redraw_mask & REDRAW_DOOR_3)
557 DrawMaskedBorderExt_DOOR_3(draw_target);
561 void DrawMaskedBorder_FIELD()
563 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
566 void DrawMaskedBorder(int redraw_mask)
568 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
571 void DrawMaskedBorderToTarget(int draw_target)
573 if (draw_target == DRAW_TO_BACKBUFFER ||
574 draw_target == DRAW_TO_SCREEN)
576 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
580 int last_border_status = global.border_status;
582 if (draw_target == DRAW_TO_FADE_SOURCE)
584 global.border_status = gfx.fade_border_source_status;
585 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
587 else if (draw_target == DRAW_TO_FADE_TARGET)
589 global.border_status = gfx.fade_border_target_status;
590 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
593 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
595 global.border_status = last_border_status;
596 gfx.masked_border_bitmap_ptr = backbuffer;
600 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
602 int fx = getFieldbufferOffsetX_RND();
603 int fy = getFieldbufferOffsetY_RND();
605 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
608 void BlitScreenToBitmap(Bitmap *target_bitmap)
610 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
611 BlitScreenToBitmap_EM(target_bitmap);
612 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
613 BlitScreenToBitmap_SP(target_bitmap);
614 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
615 BlitScreenToBitmap_RND(target_bitmap);
617 redraw_mask |= REDRAW_FIELD;
620 void DrawFramesPerSecond()
623 int font_nr = FONT_TEXT_2;
624 int font_width = getFontWidth(font_nr);
626 sprintf(text, "%04.1f fps", global.frames_per_second);
628 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
629 font_nr, BLIT_OPAQUE);
633 static void PrintFrameTimeDebugging()
635 static unsigned int last_counter = 0;
636 unsigned int counter = Counter();
637 int diff_1 = counter - last_counter;
638 int diff_2 = diff_1 - GAME_FRAME_DELAY;
640 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
641 char diff_bar[2 * diff_2_max + 5];
645 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
647 for (i = 0; i < diff_2_max; i++)
648 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
649 i >= diff_2_max - diff_2_cut ? '-' : ' ');
651 diff_bar[pos++] = '|';
653 for (i = 0; i < diff_2_max; i++)
654 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
656 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
658 diff_bar[pos++] = '\0';
660 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
663 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
666 last_counter = counter;
670 static int unifiedRedrawMask(int mask)
672 if (mask & REDRAW_ALL)
675 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
681 static boolean equalRedrawMasks(int mask_1, int mask_2)
683 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
688 static int last_redraw_mask = REDRAW_NONE;
690 // force screen redraw in every frame to continue drawing global animations
691 // (but always use the last redraw mask to prevent unwanted side effects)
692 if (redraw_mask == REDRAW_NONE)
693 redraw_mask = last_redraw_mask;
695 last_redraw_mask = redraw_mask;
698 // masked border now drawn immediately when blitting backbuffer to window
700 // draw masked border to all viewports, if defined
701 DrawMaskedBorder(redraw_mask);
704 // draw frames per second (only if debug mode is enabled)
705 if (redraw_mask & REDRAW_FPS)
706 DrawFramesPerSecond();
708 // remove playfield redraw before potentially merging with doors redraw
709 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
710 redraw_mask &= ~REDRAW_FIELD;
712 // redraw complete window if both playfield and (some) doors need redraw
713 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
714 redraw_mask = REDRAW_ALL;
716 /* although redrawing the whole window would be fine for normal gameplay,
717 being able to only redraw the playfield is required for deactivating
718 certain drawing areas (mainly playfield) to work, which is needed for
719 warp-forward to be fast enough (by skipping redraw of most frames) */
721 if (redraw_mask & REDRAW_ALL)
723 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
725 else if (redraw_mask & REDRAW_FIELD)
727 BlitBitmap(backbuffer, window,
728 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
730 else if (redraw_mask & REDRAW_DOORS)
732 // merge door areas to prevent calling screen redraw more than once
738 if (redraw_mask & REDRAW_DOOR_1)
742 x2 = MAX(x2, DX + DXSIZE);
743 y2 = MAX(y2, DY + DYSIZE);
746 if (redraw_mask & REDRAW_DOOR_2)
750 x2 = MAX(x2, VX + VXSIZE);
751 y2 = MAX(y2, VY + VYSIZE);
754 if (redraw_mask & REDRAW_DOOR_3)
758 x2 = MAX(x2, EX + EXSIZE);
759 y2 = MAX(y2, EY + EYSIZE);
762 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
765 redraw_mask = REDRAW_NONE;
768 PrintFrameTimeDebugging();
772 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
774 unsigned int frame_delay_value_old = GetVideoFrameDelay();
776 SetVideoFrameDelay(frame_delay_value);
780 SetVideoFrameDelay(frame_delay_value_old);
783 static int fade_type_skip = FADE_TYPE_NONE;
785 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
787 void (*draw_border_function)(void) = NULL;
788 int x, y, width, height;
789 int fade_delay, post_delay;
791 if (fade_type == FADE_TYPE_FADE_OUT)
793 if (fade_type_skip != FADE_TYPE_NONE)
795 /* skip all fade operations until specified fade operation */
796 if (fade_type & fade_type_skip)
797 fade_type_skip = FADE_TYPE_NONE;
802 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
806 redraw_mask |= fade_mask;
808 if (fade_type == FADE_TYPE_SKIP)
810 fade_type_skip = fade_mode;
815 fade_delay = fading.fade_delay;
816 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
818 if (fade_type_skip != FADE_TYPE_NONE)
820 /* skip all fade operations until specified fade operation */
821 if (fade_type & fade_type_skip)
822 fade_type_skip = FADE_TYPE_NONE;
827 if (global.autoplay_leveldir)
832 if (fade_mask == REDRAW_FIELD)
837 height = FADE_SYSIZE;
839 if (border.draw_masked_when_fading)
840 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
842 DrawMaskedBorder_FIELD(); /* draw once */
844 else /* REDRAW_ALL */
852 if (!setup.fade_screens ||
854 fading.fade_mode == FADE_MODE_NONE)
856 if (fade_mode == FADE_MODE_FADE_OUT)
859 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
861 redraw_mask &= ~fade_mask;
866 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
867 draw_border_function);
869 redraw_mask &= ~fade_mask;
872 static void SetScreenStates_BeforeFadingIn()
874 // temporarily set screen mode for animations to screen after fading in
875 global.anim_status = global.anim_status_next;
877 // store backbuffer with all animations that will be started after fading in
878 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
879 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
881 // set screen mode for animations back to fading
882 global.anim_status = GAME_MODE_PSEUDO_FADING;
885 static void SetScreenStates_AfterFadingIn()
887 // store new source screen (to use correct masked border for fading)
888 gfx.fade_border_source_status = global.border_status;
890 global.anim_status = global.anim_status_next;
893 static void SetScreenStates_BeforeFadingOut()
895 // store new target screen (to use correct masked border for fading)
896 gfx.fade_border_target_status = game_status;
898 // set screen mode for animations to fading
899 global.anim_status = GAME_MODE_PSEUDO_FADING;
901 // store backbuffer with all animations that will be stopped for fading out
902 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
903 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
906 static void SetScreenStates_AfterFadingOut()
908 global.border_status = game_status;
911 void FadeIn(int fade_mask)
913 SetScreenStates_BeforeFadingIn();
916 DrawMaskedBorder(REDRAW_ALL);
919 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
920 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
922 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
926 FADE_SXSIZE = FULL_SXSIZE;
927 FADE_SYSIZE = FULL_SYSIZE;
929 if (game_status == GAME_MODE_PLAYING &&
930 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
931 SetOverlayActive(TRUE);
933 SetScreenStates_AfterFadingIn();
935 // force update of global animation status in case of rapid screen changes
936 redraw_mask = REDRAW_ALL;
940 void FadeOut(int fade_mask)
942 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
943 if (!equalRedrawMasks(fade_mask, redraw_mask))
946 SetScreenStates_BeforeFadingOut();
948 SetOverlayActive(FALSE);
951 DrawMaskedBorder(REDRAW_ALL);
954 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
955 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
957 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
959 SetScreenStates_AfterFadingOut();
962 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
964 static struct TitleFadingInfo fading_leave_stored;
967 fading_leave_stored = fading_leave;
969 fading = fading_leave_stored;
972 void FadeSetEnterMenu()
974 fading = menu.enter_menu;
976 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
979 void FadeSetLeaveMenu()
981 fading = menu.leave_menu;
983 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
986 void FadeSetEnterScreen()
988 fading = menu.enter_screen[game_status];
990 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
993 void FadeSetNextScreen()
995 fading = menu.next_screen[game_status];
997 // (do not overwrite fade mode set by FadeSetEnterScreen)
998 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1001 void FadeSetLeaveScreen()
1003 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1006 void FadeSetFromType(int type)
1008 if (type & TYPE_ENTER_SCREEN)
1009 FadeSetEnterScreen();
1010 else if (type & TYPE_ENTER)
1012 else if (type & TYPE_LEAVE)
1016 void FadeSetDisabled()
1018 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1020 fading = fading_none;
1023 void FadeSkipNextFadeIn()
1025 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1028 void FadeSkipNextFadeOut()
1030 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1033 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1035 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1037 return (graphic == IMG_UNDEFINED ? NULL :
1038 graphic_info[graphic].bitmap != NULL || redefined ?
1039 graphic_info[graphic].bitmap :
1040 graphic_info[default_graphic].bitmap);
1043 Bitmap *getBackgroundBitmap(int graphic)
1045 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1048 Bitmap *getGlobalBorderBitmap(int graphic)
1050 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1053 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1056 (status == GAME_MODE_MAIN ||
1057 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1058 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1059 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1060 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1063 return getGlobalBorderBitmap(graphic);
1066 void SetWindowBackgroundImageIfDefined(int graphic)
1068 if (graphic_info[graphic].bitmap)
1069 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1072 void SetMainBackgroundImageIfDefined(int graphic)
1074 if (graphic_info[graphic].bitmap)
1075 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1078 void SetDoorBackgroundImageIfDefined(int graphic)
1080 if (graphic_info[graphic].bitmap)
1081 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1084 void SetWindowBackgroundImage(int graphic)
1086 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1089 void SetMainBackgroundImage(int graphic)
1091 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1094 void SetDoorBackgroundImage(int graphic)
1096 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1099 void SetPanelBackground()
1101 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1103 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1104 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1106 SetDoorBackgroundBitmap(bitmap_db_panel);
1109 void DrawBackground(int x, int y, int width, int height)
1111 /* "drawto" might still point to playfield buffer here (hall of fame) */
1112 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1114 if (IN_GFX_FIELD_FULL(x, y))
1115 redraw_mask |= REDRAW_FIELD;
1116 else if (IN_GFX_DOOR_1(x, y))
1117 redraw_mask |= REDRAW_DOOR_1;
1118 else if (IN_GFX_DOOR_2(x, y))
1119 redraw_mask |= REDRAW_DOOR_2;
1120 else if (IN_GFX_DOOR_3(x, y))
1121 redraw_mask |= REDRAW_DOOR_3;
1124 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1126 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1128 if (font->bitmap == NULL)
1131 DrawBackground(x, y, width, height);
1134 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1136 struct GraphicInfo *g = &graphic_info[graphic];
1138 if (g->bitmap == NULL)
1141 DrawBackground(x, y, width, height);
1144 static int game_status_last = -1;
1145 static Bitmap *global_border_bitmap_last = NULL;
1146 static Bitmap *global_border_bitmap = NULL;
1147 static int real_sx_last = -1, real_sy_last = -1;
1148 static int full_sxsize_last = -1, full_sysize_last = -1;
1149 static int dx_last = -1, dy_last = -1;
1150 static int dxsize_last = -1, dysize_last = -1;
1151 static int vx_last = -1, vy_last = -1;
1152 static int vxsize_last = -1, vysize_last = -1;
1154 boolean CheckIfGlobalBorderHasChanged()
1156 // if game status has not changed, global border has not changed either
1157 if (game_status == game_status_last)
1160 // determine and store new global border bitmap for current game status
1161 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1163 return (global_border_bitmap_last != global_border_bitmap);
1166 boolean CheckIfGlobalBorderRedrawIsNeeded()
1168 // if game status has not changed, nothing has to be redrawn
1169 if (game_status == game_status_last)
1172 // redraw if last screen was title screen
1173 if (game_status_last == GAME_MODE_TITLE)
1176 // redraw if global screen border has changed
1177 if (CheckIfGlobalBorderHasChanged())
1180 // redraw if position or size of playfield area has changed
1181 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1182 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1185 // redraw if position or size of door area has changed
1186 if (dx_last != DX || dy_last != DY ||
1187 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1190 // redraw if position or size of tape area has changed
1191 if (vx_last != VX || vy_last != VY ||
1192 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1198 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1201 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1203 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1206 void RedrawGlobalBorder()
1208 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1210 RedrawGlobalBorderFromBitmap(bitmap);
1212 redraw_mask = REDRAW_ALL;
1215 static void RedrawGlobalBorderIfNeeded()
1217 if (game_status == game_status_last)
1220 // copy current draw buffer to later copy back areas that have not changed
1221 if (game_status_last != GAME_MODE_TITLE)
1222 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1224 if (CheckIfGlobalBorderRedrawIsNeeded())
1226 // redraw global screen border (or clear, if defined to be empty)
1227 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1229 // copy previous playfield and door areas, if they are defined on both
1230 // previous and current screen and if they still have the same size
1232 if (real_sx_last != -1 && real_sy_last != -1 &&
1233 REAL_SX != -1 && REAL_SY != -1 &&
1234 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1235 BlitBitmap(bitmap_db_store_1, backbuffer,
1236 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1239 if (dx_last != -1 && dy_last != -1 &&
1240 DX != -1 && DY != -1 &&
1241 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1242 BlitBitmap(bitmap_db_store_1, backbuffer,
1243 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1245 if (vx_last != -1 && vy_last != -1 &&
1246 VX != -1 && VY != -1 &&
1247 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1248 BlitBitmap(bitmap_db_store_1, backbuffer,
1249 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1251 redraw_mask = REDRAW_ALL;
1254 game_status_last = game_status;
1256 global_border_bitmap_last = global_border_bitmap;
1258 real_sx_last = REAL_SX;
1259 real_sy_last = REAL_SY;
1260 full_sxsize_last = FULL_SXSIZE;
1261 full_sysize_last = FULL_SYSIZE;
1264 dxsize_last = DXSIZE;
1265 dysize_last = DYSIZE;
1268 vxsize_last = VXSIZE;
1269 vysize_last = VYSIZE;
1274 RedrawGlobalBorderIfNeeded();
1276 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1277 /* (when entering hall of fame after playing) */
1278 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1280 /* !!! maybe this should be done before clearing the background !!! */
1281 if (game_status == GAME_MODE_PLAYING)
1283 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1284 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1288 SetDrawtoField(DRAW_TO_BACKBUFFER);
1292 void MarkTileDirty(int x, int y)
1294 redraw_mask |= REDRAW_FIELD;
1297 void SetBorderElement()
1301 BorderElement = EL_EMPTY;
1303 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1305 for (x = 0; x < lev_fieldx; x++)
1307 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1308 BorderElement = EL_STEELWALL;
1310 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1316 void FloodFillLevel(int from_x, int from_y, int fill_element,
1317 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1318 int max_fieldx, int max_fieldy)
1322 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1323 static int safety = 0;
1325 /* check if starting field still has the desired content */
1326 if (field[from_x][from_y] == fill_element)
1331 if (safety > max_fieldx * max_fieldy)
1332 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1334 old_element = field[from_x][from_y];
1335 field[from_x][from_y] = fill_element;
1337 for (i = 0; i < 4; i++)
1339 x = from_x + check[i][0];
1340 y = from_y + check[i][1];
1342 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1343 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1349 void SetRandomAnimationValue(int x, int y)
1351 gfx.anim_random_frame = GfxRandom[x][y];
1354 int getGraphicAnimationFrame(int graphic, int sync_frame)
1356 /* animation synchronized with global frame counter, not move position */
1357 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1358 sync_frame = FrameCounter;
1360 return getAnimationFrame(graphic_info[graphic].anim_frames,
1361 graphic_info[graphic].anim_delay,
1362 graphic_info[graphic].anim_mode,
1363 graphic_info[graphic].anim_start_frame,
1367 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1369 struct GraphicInfo *g = &graphic_info[graphic];
1370 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1372 if (tilesize == gfx.standard_tile_size)
1373 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1374 else if (tilesize == game.tile_size)
1375 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1377 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1380 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1381 boolean get_backside)
1383 struct GraphicInfo *g = &graphic_info[graphic];
1384 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1385 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1387 if (g->offset_y == 0) /* frames are ordered horizontally */
1389 int max_width = g->anim_frames_per_line * g->width;
1390 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1392 *x = pos % max_width;
1393 *y = src_y % g->height + pos / max_width * g->height;
1395 else if (g->offset_x == 0) /* frames are ordered vertically */
1397 int max_height = g->anim_frames_per_line * g->height;
1398 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1400 *x = src_x % g->width + pos / max_height * g->width;
1401 *y = pos % max_height;
1403 else /* frames are ordered diagonally */
1405 *x = src_x + frame * g->offset_x;
1406 *y = src_y + frame * g->offset_y;
1410 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1411 Bitmap **bitmap, int *x, int *y,
1412 boolean get_backside)
1414 struct GraphicInfo *g = &graphic_info[graphic];
1416 // if no in-game graphics defined, always use standard graphic size
1417 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1418 tilesize = TILESIZE;
1420 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1421 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1423 *x = *x * tilesize / g->tile_size;
1424 *y = *y * tilesize / g->tile_size;
1427 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1428 int *x, int *y, boolean get_backside)
1430 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1434 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1435 Bitmap **bitmap, int *x, int *y)
1437 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1440 void getFixedGraphicSource(int graphic, int frame,
1441 Bitmap **bitmap, int *x, int *y)
1443 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1446 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1448 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1451 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1452 int *x, int *y, boolean get_backside)
1454 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1458 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1460 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1463 void DrawGraphic(int x, int y, int graphic, int frame)
1466 if (!IN_SCR_FIELD(x, y))
1468 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1469 printf("DrawGraphic(): This should never happen!\n");
1474 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1477 MarkTileDirty(x, y);
1480 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1483 if (!IN_SCR_FIELD(x, y))
1485 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1486 printf("DrawGraphic(): This should never happen!\n");
1491 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1493 MarkTileDirty(x, y);
1496 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1502 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1504 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1507 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1513 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1514 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1517 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1520 if (!IN_SCR_FIELD(x, y))
1522 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1523 printf("DrawGraphicThruMask(): This should never happen!\n");
1528 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1531 MarkTileDirty(x, y);
1534 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1537 if (!IN_SCR_FIELD(x, y))
1539 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1540 printf("DrawGraphicThruMask(): This should never happen!\n");
1545 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1547 MarkTileDirty(x, y);
1550 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1556 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1558 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1562 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1563 int graphic, int frame)
1568 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1570 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1574 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1576 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1578 MarkTileDirty(x / tilesize, y / tilesize);
1581 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1587 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1588 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1591 void DrawMiniGraphic(int x, int y, int graphic)
1593 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1594 MarkTileDirty(x / 2, y / 2);
1597 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1602 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1603 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1606 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1607 int graphic, int frame,
1608 int cut_mode, int mask_mode)
1613 int width = TILEX, height = TILEY;
1616 if (dx || dy) /* shifted graphic */
1618 if (x < BX1) /* object enters playfield from the left */
1625 else if (x > BX2) /* object enters playfield from the right */
1631 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1637 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1639 else if (dx) /* general horizontal movement */
1640 MarkTileDirty(x + SIGN(dx), y);
1642 if (y < BY1) /* object enters playfield from the top */
1644 if (cut_mode == CUT_BELOW) /* object completely above top border */
1652 else if (y > BY2) /* object enters playfield from the bottom */
1658 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1664 else if (dy > 0 && cut_mode == CUT_ABOVE)
1666 if (y == BY2) /* object completely above bottom border */
1672 MarkTileDirty(x, y + 1);
1673 } /* object leaves playfield to the bottom */
1674 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1676 else if (dy) /* general vertical movement */
1677 MarkTileDirty(x, y + SIGN(dy));
1681 if (!IN_SCR_FIELD(x, y))
1683 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1684 printf("DrawGraphicShifted(): This should never happen!\n");
1689 width = width * TILESIZE_VAR / TILESIZE;
1690 height = height * TILESIZE_VAR / TILESIZE;
1691 cx = cx * TILESIZE_VAR / TILESIZE;
1692 cy = cy * TILESIZE_VAR / TILESIZE;
1693 dx = dx * TILESIZE_VAR / TILESIZE;
1694 dy = dy * TILESIZE_VAR / TILESIZE;
1696 if (width > 0 && height > 0)
1698 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1703 dst_x = FX + x * TILEX_VAR + dx;
1704 dst_y = FY + y * TILEY_VAR + dy;
1706 if (mask_mode == USE_MASKING)
1707 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1710 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1713 MarkTileDirty(x, y);
1717 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1718 int graphic, int frame,
1719 int cut_mode, int mask_mode)
1724 int width = TILEX_VAR, height = TILEY_VAR;
1727 int x2 = x + SIGN(dx);
1728 int y2 = y + SIGN(dy);
1730 /* movement with two-tile animations must be sync'ed with movement position,
1731 not with current GfxFrame (which can be higher when using slow movement) */
1732 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1733 int anim_frames = graphic_info[graphic].anim_frames;
1735 /* (we also need anim_delay here for movement animations with less frames) */
1736 int anim_delay = graphic_info[graphic].anim_delay;
1737 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1739 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1740 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1742 /* re-calculate animation frame for two-tile movement animation */
1743 frame = getGraphicAnimationFrame(graphic, sync_frame);
1745 /* check if movement start graphic inside screen area and should be drawn */
1746 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1748 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1750 dst_x = FX + x1 * TILEX_VAR;
1751 dst_y = FY + y1 * TILEY_VAR;
1753 if (mask_mode == USE_MASKING)
1754 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1757 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1760 MarkTileDirty(x1, y1);
1763 /* check if movement end graphic inside screen area and should be drawn */
1764 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1766 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1768 dst_x = FX + x2 * TILEX_VAR;
1769 dst_y = FY + y2 * TILEY_VAR;
1771 if (mask_mode == USE_MASKING)
1772 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1775 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1778 MarkTileDirty(x2, y2);
1782 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1783 int graphic, int frame,
1784 int cut_mode, int mask_mode)
1788 DrawGraphic(x, y, graphic, frame);
1793 if (graphic_info[graphic].double_movement) /* EM style movement images */
1794 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1796 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1799 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1800 int frame, int cut_mode)
1802 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1805 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1806 int cut_mode, int mask_mode)
1808 int lx = LEVELX(x), ly = LEVELY(y);
1812 if (IN_LEV_FIELD(lx, ly))
1814 SetRandomAnimationValue(lx, ly);
1816 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1817 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1819 /* do not use double (EM style) movement graphic when not moving */
1820 if (graphic_info[graphic].double_movement && !dx && !dy)
1822 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1823 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1826 else /* border element */
1828 graphic = el2img(element);
1829 frame = getGraphicAnimationFrame(graphic, -1);
1832 if (element == EL_EXPANDABLE_WALL)
1834 boolean left_stopped = FALSE, right_stopped = FALSE;
1836 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1837 left_stopped = TRUE;
1838 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1839 right_stopped = TRUE;
1841 if (left_stopped && right_stopped)
1843 else if (left_stopped)
1845 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1846 frame = graphic_info[graphic].anim_frames - 1;
1848 else if (right_stopped)
1850 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1851 frame = graphic_info[graphic].anim_frames - 1;
1856 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1857 else if (mask_mode == USE_MASKING)
1858 DrawGraphicThruMask(x, y, graphic, frame);
1860 DrawGraphic(x, y, graphic, frame);
1863 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1864 int cut_mode, int mask_mode)
1866 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1867 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1868 cut_mode, mask_mode);
1871 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1874 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1877 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1880 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1883 void DrawLevelElementThruMask(int x, int y, int element)
1885 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1888 void DrawLevelFieldThruMask(int x, int y)
1890 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1893 /* !!! implementation of quicksand is totally broken !!! */
1894 #define IS_CRUMBLED_TILE(x, y, e) \
1895 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1896 !IS_MOVING(x, y) || \
1897 (e) == EL_QUICKSAND_EMPTYING || \
1898 (e) == EL_QUICKSAND_FAST_EMPTYING))
1900 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1905 int width, height, cx, cy;
1906 int sx = SCREENX(x), sy = SCREENY(y);
1907 int crumbled_border_size = graphic_info[graphic].border_size;
1908 int crumbled_tile_size = graphic_info[graphic].tile_size;
1909 int crumbled_border_size_var =
1910 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1913 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1915 for (i = 1; i < 4; i++)
1917 int dxx = (i & 1 ? dx : 0);
1918 int dyy = (i & 2 ? dy : 0);
1921 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1924 /* check if neighbour field is of same crumble type */
1925 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1926 graphic_info[graphic].class ==
1927 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1929 /* return if check prevents inner corner */
1930 if (same == (dxx == dx && dyy == dy))
1934 /* if we reach this point, we have an inner corner */
1936 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1938 width = crumbled_border_size_var;
1939 height = crumbled_border_size_var;
1940 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1941 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1943 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1944 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1947 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1952 int width, height, bx, by, cx, cy;
1953 int sx = SCREENX(x), sy = SCREENY(y);
1954 int crumbled_border_size = graphic_info[graphic].border_size;
1955 int crumbled_tile_size = graphic_info[graphic].tile_size;
1956 int crumbled_border_size_var =
1957 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1958 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1961 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1963 /* draw simple, sloppy, non-corner-accurate crumbled border */
1965 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1966 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1967 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1968 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1970 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1971 FX + sx * TILEX_VAR + cx,
1972 FY + sy * TILEY_VAR + cy);
1974 /* (remaining middle border part must be at least as big as corner part) */
1975 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1976 crumbled_border_size_var >= TILESIZE_VAR / 3)
1979 /* correct corners of crumbled border, if needed */
1981 for (i = -1; i <= 1; i += 2)
1983 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1984 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1985 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1988 /* check if neighbour field is of same crumble type */
1989 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1990 graphic_info[graphic].class ==
1991 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1993 /* no crumbled corner, but continued crumbled border */
1995 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1996 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1997 int b1 = (i == 1 ? crumbled_border_size_var :
1998 TILESIZE_VAR - 2 * crumbled_border_size_var);
2000 width = crumbled_border_size_var;
2001 height = crumbled_border_size_var;
2003 if (dir == 1 || dir == 2)
2018 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2020 FX + sx * TILEX_VAR + cx,
2021 FY + sy * TILEY_VAR + cy);
2026 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2028 int sx = SCREENX(x), sy = SCREENY(y);
2031 static int xy[4][2] =
2039 if (!IN_LEV_FIELD(x, y))
2042 element = TILE_GFX_ELEMENT(x, y);
2044 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2046 if (!IN_SCR_FIELD(sx, sy))
2049 /* crumble field borders towards direct neighbour fields */
2050 for (i = 0; i < 4; i++)
2052 int xx = x + xy[i][0];
2053 int yy = y + xy[i][1];
2055 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2058 /* check if neighbour field is of same crumble type */
2059 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2060 graphic_info[graphic].class ==
2061 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2064 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2067 /* crumble inner field corners towards corner neighbour fields */
2068 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2069 graphic_info[graphic].anim_frames == 2)
2071 for (i = 0; i < 4; i++)
2073 int dx = (i & 1 ? +1 : -1);
2074 int dy = (i & 2 ? +1 : -1);
2076 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2080 MarkTileDirty(sx, sy);
2082 else /* center field is not crumbled -- crumble neighbour fields */
2084 /* crumble field borders of direct neighbour fields */
2085 for (i = 0; i < 4; i++)
2087 int xx = x + xy[i][0];
2088 int yy = y + xy[i][1];
2089 int sxx = sx + xy[i][0];
2090 int syy = sy + xy[i][1];
2092 if (!IN_LEV_FIELD(xx, yy) ||
2093 !IN_SCR_FIELD(sxx, syy))
2096 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2099 element = TILE_GFX_ELEMENT(xx, yy);
2101 if (!IS_CRUMBLED_TILE(xx, yy, element))
2104 graphic = el_act2crm(element, ACTION_DEFAULT);
2106 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2108 MarkTileDirty(sxx, syy);
2111 /* crumble inner field corners of corner neighbour fields */
2112 for (i = 0; i < 4; i++)
2114 int dx = (i & 1 ? +1 : -1);
2115 int dy = (i & 2 ? +1 : -1);
2121 if (!IN_LEV_FIELD(xx, yy) ||
2122 !IN_SCR_FIELD(sxx, syy))
2125 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2128 element = TILE_GFX_ELEMENT(xx, yy);
2130 if (!IS_CRUMBLED_TILE(xx, yy, element))
2133 graphic = el_act2crm(element, ACTION_DEFAULT);
2135 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2136 graphic_info[graphic].anim_frames == 2)
2137 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2139 MarkTileDirty(sxx, syy);
2144 void DrawLevelFieldCrumbled(int x, int y)
2148 if (!IN_LEV_FIELD(x, y))
2151 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2152 GfxElement[x][y] != EL_UNDEFINED &&
2153 GFX_CRUMBLED(GfxElement[x][y]))
2155 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2160 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2162 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2165 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2168 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2169 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2170 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2171 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2172 int sx = SCREENX(x), sy = SCREENY(y);
2174 DrawGraphic(sx, sy, graphic1, frame1);
2175 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2178 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2180 int sx = SCREENX(x), sy = SCREENY(y);
2181 static int xy[4][2] =
2190 /* crumble direct neighbour fields (required for field borders) */
2191 for (i = 0; i < 4; i++)
2193 int xx = x + xy[i][0];
2194 int yy = y + xy[i][1];
2195 int sxx = sx + xy[i][0];
2196 int syy = sy + xy[i][1];
2198 if (!IN_LEV_FIELD(xx, yy) ||
2199 !IN_SCR_FIELD(sxx, syy) ||
2200 !GFX_CRUMBLED(Feld[xx][yy]) ||
2204 DrawLevelField(xx, yy);
2207 /* crumble corner neighbour fields (required for inner field corners) */
2208 for (i = 0; i < 4; i++)
2210 int dx = (i & 1 ? +1 : -1);
2211 int dy = (i & 2 ? +1 : -1);
2217 if (!IN_LEV_FIELD(xx, yy) ||
2218 !IN_SCR_FIELD(sxx, syy) ||
2219 !GFX_CRUMBLED(Feld[xx][yy]) ||
2223 int element = TILE_GFX_ELEMENT(xx, yy);
2224 int graphic = el_act2crm(element, ACTION_DEFAULT);
2226 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2227 graphic_info[graphic].anim_frames == 2)
2228 DrawLevelField(xx, yy);
2232 static int getBorderElement(int x, int y)
2236 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2237 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2238 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2239 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2240 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2241 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2242 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2244 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2245 int steel_position = (x == -1 && y == -1 ? 0 :
2246 x == lev_fieldx && y == -1 ? 1 :
2247 x == -1 && y == lev_fieldy ? 2 :
2248 x == lev_fieldx && y == lev_fieldy ? 3 :
2249 x == -1 || x == lev_fieldx ? 4 :
2250 y == -1 || y == lev_fieldy ? 5 : 6);
2252 return border[steel_position][steel_type];
2255 void DrawScreenElement(int x, int y, int element)
2257 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2258 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2261 void DrawLevelElement(int x, int y, int element)
2263 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2264 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2267 void DrawScreenField(int x, int y)
2269 int lx = LEVELX(x), ly = LEVELY(y);
2270 int element, content;
2272 if (!IN_LEV_FIELD(lx, ly))
2274 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2277 element = getBorderElement(lx, ly);
2279 DrawScreenElement(x, y, element);
2284 element = Feld[lx][ly];
2285 content = Store[lx][ly];
2287 if (IS_MOVING(lx, ly))
2289 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2290 boolean cut_mode = NO_CUTTING;
2292 if (element == EL_QUICKSAND_EMPTYING ||
2293 element == EL_QUICKSAND_FAST_EMPTYING ||
2294 element == EL_MAGIC_WALL_EMPTYING ||
2295 element == EL_BD_MAGIC_WALL_EMPTYING ||
2296 element == EL_DC_MAGIC_WALL_EMPTYING ||
2297 element == EL_AMOEBA_DROPPING)
2298 cut_mode = CUT_ABOVE;
2299 else if (element == EL_QUICKSAND_FILLING ||
2300 element == EL_QUICKSAND_FAST_FILLING ||
2301 element == EL_MAGIC_WALL_FILLING ||
2302 element == EL_BD_MAGIC_WALL_FILLING ||
2303 element == EL_DC_MAGIC_WALL_FILLING)
2304 cut_mode = CUT_BELOW;
2306 if (cut_mode == CUT_ABOVE)
2307 DrawScreenElement(x, y, element);
2309 DrawScreenElement(x, y, EL_EMPTY);
2312 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2313 else if (cut_mode == NO_CUTTING)
2314 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2317 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2319 if (cut_mode == CUT_BELOW &&
2320 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2321 DrawLevelElement(lx, ly + 1, element);
2324 if (content == EL_ACID)
2326 int dir = MovDir[lx][ly];
2327 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2328 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2330 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2332 // prevent target field from being drawn again (but without masking)
2333 // (this would happen if target field is scanned after moving element)
2334 Stop[newlx][newly] = TRUE;
2337 else if (IS_BLOCKED(lx, ly))
2342 boolean cut_mode = NO_CUTTING;
2343 int element_old, content_old;
2345 Blocked2Moving(lx, ly, &oldx, &oldy);
2348 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2349 MovDir[oldx][oldy] == MV_RIGHT);
2351 element_old = Feld[oldx][oldy];
2352 content_old = Store[oldx][oldy];
2354 if (element_old == EL_QUICKSAND_EMPTYING ||
2355 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2356 element_old == EL_MAGIC_WALL_EMPTYING ||
2357 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2358 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2359 element_old == EL_AMOEBA_DROPPING)
2360 cut_mode = CUT_ABOVE;
2362 DrawScreenElement(x, y, EL_EMPTY);
2365 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2367 else if (cut_mode == NO_CUTTING)
2368 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2371 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2374 else if (IS_DRAWABLE(element))
2375 DrawScreenElement(x, y, element);
2377 DrawScreenElement(x, y, EL_EMPTY);
2380 void DrawLevelField(int x, int y)
2382 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2383 DrawScreenField(SCREENX(x), SCREENY(y));
2384 else if (IS_MOVING(x, y))
2388 Moving2Blocked(x, y, &newx, &newy);
2389 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2390 DrawScreenField(SCREENX(newx), SCREENY(newy));
2392 else if (IS_BLOCKED(x, y))
2396 Blocked2Moving(x, y, &oldx, &oldy);
2397 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2398 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2402 void DrawSizedElement(int x, int y, int element, int tilesize)
2406 graphic = el2edimg(element);
2407 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2410 void DrawMiniElement(int x, int y, int element)
2414 graphic = el2edimg(element);
2415 DrawMiniGraphic(x, y, graphic);
2418 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2421 int x = sx + scroll_x, y = sy + scroll_y;
2423 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2424 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2425 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2426 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2428 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2431 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2433 int x = sx + scroll_x, y = sy + scroll_y;
2435 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2436 DrawMiniElement(sx, sy, EL_EMPTY);
2437 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2438 DrawMiniElement(sx, sy, Feld[x][y]);
2440 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2443 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2444 int x, int y, int xsize, int ysize,
2445 int tile_width, int tile_height)
2449 int dst_x = startx + x * tile_width;
2450 int dst_y = starty + y * tile_height;
2451 int width = graphic_info[graphic].width;
2452 int height = graphic_info[graphic].height;
2453 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2454 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2455 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2456 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2457 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2458 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2459 boolean draw_masked = graphic_info[graphic].draw_masked;
2461 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2463 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2465 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2469 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2470 inner_sx + (x - 1) * tile_width % inner_width);
2471 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2472 inner_sy + (y - 1) * tile_height % inner_height);
2475 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2478 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2482 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2483 int x, int y, int xsize, int ysize, int font_nr)
2485 int font_width = getFontWidth(font_nr);
2486 int font_height = getFontHeight(font_nr);
2488 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2489 font_width, font_height);
2492 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2494 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2495 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2496 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2497 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2498 boolean no_delay = (tape.warp_forward);
2499 unsigned int anim_delay = 0;
2500 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2501 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2502 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2503 int font_width = getFontWidth(font_nr);
2504 int font_height = getFontHeight(font_nr);
2505 int max_xsize = level.envelope[envelope_nr].xsize;
2506 int max_ysize = level.envelope[envelope_nr].ysize;
2507 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2508 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2509 int xend = max_xsize;
2510 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2511 int xstep = (xstart < xend ? 1 : 0);
2512 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2514 int end = MAX(xend - xstart, yend - ystart);
2517 for (i = start; i <= end; i++)
2519 int last_frame = end; // last frame of this "for" loop
2520 int x = xstart + i * xstep;
2521 int y = ystart + i * ystep;
2522 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2523 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2524 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2525 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2528 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2530 BlitScreenToBitmap(backbuffer);
2532 SetDrawtoField(DRAW_TO_BACKBUFFER);
2534 for (yy = 0; yy < ysize; yy++)
2535 for (xx = 0; xx < xsize; xx++)
2536 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2538 DrawTextBuffer(sx + font_width, sy + font_height,
2539 level.envelope[envelope_nr].text, font_nr, max_xsize,
2540 xsize - 2, ysize - 2, 0, mask_mode,
2541 level.envelope[envelope_nr].autowrap,
2542 level.envelope[envelope_nr].centered, FALSE);
2544 redraw_mask |= REDRAW_FIELD;
2547 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2551 void ShowEnvelope(int envelope_nr)
2553 int element = EL_ENVELOPE_1 + envelope_nr;
2554 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2555 int sound_opening = element_info[element].sound[ACTION_OPENING];
2556 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2557 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2558 boolean no_delay = (tape.warp_forward);
2559 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2560 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2561 int anim_mode = graphic_info[graphic].anim_mode;
2562 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2563 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2565 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2567 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2569 if (anim_mode == ANIM_DEFAULT)
2570 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2572 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2575 Delay(wait_delay_value);
2577 WaitForEventToContinue();
2579 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2581 if (anim_mode != ANIM_NONE)
2582 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2584 if (anim_mode == ANIM_DEFAULT)
2585 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2587 game.envelope_active = FALSE;
2589 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2591 redraw_mask |= REDRAW_FIELD;
2595 static void setRequestBasePosition(int *x, int *y)
2597 int sx_base, sy_base;
2599 if (request.x != -1)
2600 sx_base = request.x;
2601 else if (request.align == ALIGN_LEFT)
2603 else if (request.align == ALIGN_RIGHT)
2604 sx_base = SX + SXSIZE;
2606 sx_base = SX + SXSIZE / 2;
2608 if (request.y != -1)
2609 sy_base = request.y;
2610 else if (request.valign == VALIGN_TOP)
2612 else if (request.valign == VALIGN_BOTTOM)
2613 sy_base = SY + SYSIZE;
2615 sy_base = SY + SYSIZE / 2;
2621 static void setRequestPositionExt(int *x, int *y, int width, int height,
2622 boolean add_border_size)
2624 int border_size = request.border_size;
2625 int sx_base, sy_base;
2628 setRequestBasePosition(&sx_base, &sy_base);
2630 if (request.align == ALIGN_LEFT)
2632 else if (request.align == ALIGN_RIGHT)
2633 sx = sx_base - width;
2635 sx = sx_base - width / 2;
2637 if (request.valign == VALIGN_TOP)
2639 else if (request.valign == VALIGN_BOTTOM)
2640 sy = sy_base - height;
2642 sy = sy_base - height / 2;
2644 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2645 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2647 if (add_border_size)
2657 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2659 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2662 void DrawEnvelopeRequest(char *text)
2664 char *text_final = text;
2665 char *text_door_style = NULL;
2666 int graphic = IMG_BACKGROUND_REQUEST;
2667 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2668 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2669 int font_nr = FONT_REQUEST;
2670 int font_width = getFontWidth(font_nr);
2671 int font_height = getFontHeight(font_nr);
2672 int border_size = request.border_size;
2673 int line_spacing = request.line_spacing;
2674 int line_height = font_height + line_spacing;
2675 int max_text_width = request.width - 2 * border_size;
2676 int max_text_height = request.height - 2 * border_size;
2677 int line_length = max_text_width / font_width;
2678 int max_lines = max_text_height / line_height;
2679 int text_width = line_length * font_width;
2680 int width = request.width;
2681 int height = request.height;
2682 int tile_size = MAX(request.step_offset, 1);
2683 int x_steps = width / tile_size;
2684 int y_steps = height / tile_size;
2685 int sx_offset = border_size;
2686 int sy_offset = border_size;
2690 if (request.centered)
2691 sx_offset = (request.width - text_width) / 2;
2693 if (request.wrap_single_words && !request.autowrap)
2695 char *src_text_ptr, *dst_text_ptr;
2697 text_door_style = checked_malloc(2 * strlen(text) + 1);
2699 src_text_ptr = text;
2700 dst_text_ptr = text_door_style;
2702 while (*src_text_ptr)
2704 if (*src_text_ptr == ' ' ||
2705 *src_text_ptr == '?' ||
2706 *src_text_ptr == '!')
2707 *dst_text_ptr++ = '\n';
2709 if (*src_text_ptr != ' ')
2710 *dst_text_ptr++ = *src_text_ptr;
2715 *dst_text_ptr = '\0';
2717 text_final = text_door_style;
2720 setRequestPosition(&sx, &sy, FALSE);
2722 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2724 for (y = 0; y < y_steps; y++)
2725 for (x = 0; x < x_steps; x++)
2726 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2727 x, y, x_steps, y_steps,
2728 tile_size, tile_size);
2730 /* force DOOR font inside door area */
2731 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2733 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2734 line_length, -1, max_lines, line_spacing, mask_mode,
2735 request.autowrap, request.centered, FALSE);
2739 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2740 RedrawGadget(tool_gadget[i]);
2742 // store readily prepared envelope request for later use when animating
2743 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2745 if (text_door_style)
2746 free(text_door_style);
2749 void AnimateEnvelopeRequest(int anim_mode, int action)
2751 int graphic = IMG_BACKGROUND_REQUEST;
2752 boolean draw_masked = graphic_info[graphic].draw_masked;
2753 int delay_value_normal = request.step_delay;
2754 int delay_value_fast = delay_value_normal / 2;
2755 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2756 boolean no_delay = (tape.warp_forward);
2757 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2758 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2759 unsigned int anim_delay = 0;
2761 int tile_size = MAX(request.step_offset, 1);
2762 int max_xsize = request.width / tile_size;
2763 int max_ysize = request.height / tile_size;
2764 int max_xsize_inner = max_xsize - 2;
2765 int max_ysize_inner = max_ysize - 2;
2767 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2768 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2769 int xend = max_xsize_inner;
2770 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2771 int xstep = (xstart < xend ? 1 : 0);
2772 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2774 int end = MAX(xend - xstart, yend - ystart);
2777 if (setup.quick_doors)
2784 for (i = start; i <= end; i++)
2786 int last_frame = end; // last frame of this "for" loop
2787 int x = xstart + i * xstep;
2788 int y = ystart + i * ystep;
2789 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2790 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2791 int xsize_size_left = (xsize - 1) * tile_size;
2792 int ysize_size_top = (ysize - 1) * tile_size;
2793 int max_xsize_pos = (max_xsize - 1) * tile_size;
2794 int max_ysize_pos = (max_ysize - 1) * tile_size;
2795 int width = xsize * tile_size;
2796 int height = ysize * tile_size;
2801 setRequestPosition(&src_x, &src_y, FALSE);
2802 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2804 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2806 for (yy = 0; yy < 2; yy++)
2808 for (xx = 0; xx < 2; xx++)
2810 int src_xx = src_x + xx * max_xsize_pos;
2811 int src_yy = src_y + yy * max_ysize_pos;
2812 int dst_xx = dst_x + xx * xsize_size_left;
2813 int dst_yy = dst_y + yy * ysize_size_top;
2814 int xx_size = (xx ? tile_size : xsize_size_left);
2815 int yy_size = (yy ? tile_size : ysize_size_top);
2818 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2819 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2821 BlitBitmap(bitmap_db_store_2, backbuffer,
2822 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2826 redraw_mask |= REDRAW_FIELD;
2830 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2834 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2836 int graphic = IMG_BACKGROUND_REQUEST;
2837 int sound_opening = SND_REQUEST_OPENING;
2838 int sound_closing = SND_REQUEST_CLOSING;
2839 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2840 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2841 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2842 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2843 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2845 if (game_status == GAME_MODE_PLAYING)
2846 BlitScreenToBitmap(backbuffer);
2848 SetDrawtoField(DRAW_TO_BACKBUFFER);
2850 // SetDrawBackgroundMask(REDRAW_NONE);
2852 if (action == ACTION_OPENING)
2854 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2856 if (req_state & REQ_ASK)
2858 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2859 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2861 else if (req_state & REQ_CONFIRM)
2863 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2865 else if (req_state & REQ_PLAYER)
2867 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2868 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2869 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2870 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2873 DrawEnvelopeRequest(text);
2876 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2878 if (action == ACTION_OPENING)
2880 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2882 if (anim_mode == ANIM_DEFAULT)
2883 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2885 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2889 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2891 if (anim_mode != ANIM_NONE)
2892 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2894 if (anim_mode == ANIM_DEFAULT)
2895 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2898 game.envelope_active = FALSE;
2900 if (action == ACTION_CLOSING)
2901 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2903 // SetDrawBackgroundMask(last_draw_background_mask);
2905 redraw_mask |= REDRAW_FIELD;
2909 if (action == ACTION_CLOSING &&
2910 game_status == GAME_MODE_PLAYING &&
2911 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2912 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2915 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2919 int graphic = el2preimg(element);
2921 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2922 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2925 void DrawLevel(int draw_background_mask)
2929 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2930 SetDrawBackgroundMask(draw_background_mask);
2934 for (x = BX1; x <= BX2; x++)
2935 for (y = BY1; y <= BY2; y++)
2936 DrawScreenField(x, y);
2938 redraw_mask |= REDRAW_FIELD;
2941 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2946 for (x = 0; x < size_x; x++)
2947 for (y = 0; y < size_y; y++)
2948 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2950 redraw_mask |= REDRAW_FIELD;
2953 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2957 for (x = 0; x < size_x; x++)
2958 for (y = 0; y < size_y; y++)
2959 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2961 redraw_mask |= REDRAW_FIELD;
2964 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2966 boolean show_level_border = (BorderElement != EL_EMPTY);
2967 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2968 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2969 int tile_size = preview.tile_size;
2970 int preview_width = preview.xsize * tile_size;
2971 int preview_height = preview.ysize * tile_size;
2972 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2973 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2974 int real_preview_width = real_preview_xsize * tile_size;
2975 int real_preview_height = real_preview_ysize * tile_size;
2976 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2977 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2980 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2983 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2985 dst_x += (preview_width - real_preview_width) / 2;
2986 dst_y += (preview_height - real_preview_height) / 2;
2988 for (x = 0; x < real_preview_xsize; x++)
2990 for (y = 0; y < real_preview_ysize; y++)
2992 int lx = from_x + x + (show_level_border ? -1 : 0);
2993 int ly = from_y + y + (show_level_border ? -1 : 0);
2994 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2995 getBorderElement(lx, ly));
2997 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2998 element, tile_size);
3002 redraw_mask |= REDRAW_FIELD;
3005 #define MICROLABEL_EMPTY 0
3006 #define MICROLABEL_LEVEL_NAME 1
3007 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3008 #define MICROLABEL_LEVEL_AUTHOR 3
3009 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3010 #define MICROLABEL_IMPORTED_FROM 5
3011 #define MICROLABEL_IMPORTED_BY_HEAD 6
3012 #define MICROLABEL_IMPORTED_BY 7
3014 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3016 int max_text_width = SXSIZE;
3017 int font_width = getFontWidth(font_nr);
3019 if (pos->align == ALIGN_CENTER)
3020 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3021 else if (pos->align == ALIGN_RIGHT)
3022 max_text_width = pos->x;
3024 max_text_width = SXSIZE - pos->x;
3026 return max_text_width / font_width;
3029 static void DrawPreviewLevelLabelExt(int mode)
3031 struct TextPosInfo *pos = &menu.main.text.level_info_2;
3032 char label_text[MAX_OUTPUT_LINESIZE + 1];
3033 int max_len_label_text;
3034 int font_nr = pos->font;
3037 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3040 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3041 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3042 mode == MICROLABEL_IMPORTED_BY_HEAD)
3043 font_nr = pos->font_alt;
3045 max_len_label_text = getMaxTextLength(pos, font_nr);
3047 if (pos->size != -1)
3048 max_len_label_text = pos->size;
3050 for (i = 0; i < max_len_label_text; i++)
3051 label_text[i] = ' ';
3052 label_text[max_len_label_text] = '\0';
3054 if (strlen(label_text) > 0)
3055 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3058 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3059 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3060 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3061 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3062 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3063 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3064 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3065 max_len_label_text);
3066 label_text[max_len_label_text] = '\0';
3068 if (strlen(label_text) > 0)
3069 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3071 redraw_mask |= REDRAW_FIELD;
3074 static void DrawPreviewLevelExt(boolean restart)
3076 static unsigned int scroll_delay = 0;
3077 static unsigned int label_delay = 0;
3078 static int from_x, from_y, scroll_direction;
3079 static int label_state, label_counter;
3080 unsigned int scroll_delay_value = preview.step_delay;
3081 boolean show_level_border = (BorderElement != EL_EMPTY);
3082 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3083 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3090 if (preview.anim_mode == ANIM_CENTERED)
3092 if (level_xsize > preview.xsize)
3093 from_x = (level_xsize - preview.xsize) / 2;
3094 if (level_ysize > preview.ysize)
3095 from_y = (level_ysize - preview.ysize) / 2;
3098 from_x += preview.xoffset;
3099 from_y += preview.yoffset;
3101 scroll_direction = MV_RIGHT;
3105 DrawPreviewLevelPlayfieldExt(from_x, from_y);
3106 DrawPreviewLevelLabelExt(label_state);
3108 /* initialize delay counters */
3109 DelayReached(&scroll_delay, 0);
3110 DelayReached(&label_delay, 0);
3112 if (leveldir_current->name)
3114 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3115 char label_text[MAX_OUTPUT_LINESIZE + 1];
3116 int font_nr = pos->font;
3117 int max_len_label_text = getMaxTextLength(pos, font_nr);
3119 if (pos->size != -1)
3120 max_len_label_text = pos->size;
3122 strncpy(label_text, leveldir_current->name, max_len_label_text);
3123 label_text[max_len_label_text] = '\0';
3125 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3126 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3132 /* scroll preview level, if needed */
3133 if (preview.anim_mode != ANIM_NONE &&
3134 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3135 DelayReached(&scroll_delay, scroll_delay_value))
3137 switch (scroll_direction)
3142 from_x -= preview.step_offset;
3143 from_x = (from_x < 0 ? 0 : from_x);
3146 scroll_direction = MV_UP;
3150 if (from_x < level_xsize - preview.xsize)
3152 from_x += preview.step_offset;
3153 from_x = (from_x > level_xsize - preview.xsize ?
3154 level_xsize - preview.xsize : from_x);
3157 scroll_direction = MV_DOWN;
3163 from_y -= preview.step_offset;
3164 from_y = (from_y < 0 ? 0 : from_y);
3167 scroll_direction = MV_RIGHT;
3171 if (from_y < level_ysize - preview.ysize)
3173 from_y += preview.step_offset;
3174 from_y = (from_y > level_ysize - preview.ysize ?
3175 level_ysize - preview.ysize : from_y);
3178 scroll_direction = MV_LEFT;
3185 DrawPreviewLevelPlayfieldExt(from_x, from_y);
3188 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3189 /* redraw micro level label, if needed */
3190 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3191 !strEqual(level.author, ANONYMOUS_NAME) &&
3192 !strEqual(level.author, leveldir_current->name) &&
3193 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3195 int max_label_counter = 23;
3197 if (leveldir_current->imported_from != NULL &&
3198 strlen(leveldir_current->imported_from) > 0)
3199 max_label_counter += 14;
3200 if (leveldir_current->imported_by != NULL &&
3201 strlen(leveldir_current->imported_by) > 0)
3202 max_label_counter += 14;
3204 label_counter = (label_counter + 1) % max_label_counter;
3205 label_state = (label_counter >= 0 && label_counter <= 7 ?
3206 MICROLABEL_LEVEL_NAME :
3207 label_counter >= 9 && label_counter <= 12 ?
3208 MICROLABEL_LEVEL_AUTHOR_HEAD :
3209 label_counter >= 14 && label_counter <= 21 ?
3210 MICROLABEL_LEVEL_AUTHOR :
3211 label_counter >= 23 && label_counter <= 26 ?
3212 MICROLABEL_IMPORTED_FROM_HEAD :
3213 label_counter >= 28 && label_counter <= 35 ?
3214 MICROLABEL_IMPORTED_FROM :
3215 label_counter >= 37 && label_counter <= 40 ?
3216 MICROLABEL_IMPORTED_BY_HEAD :
3217 label_counter >= 42 && label_counter <= 49 ?
3218 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3220 if (leveldir_current->imported_from == NULL &&
3221 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3222 label_state == MICROLABEL_IMPORTED_FROM))
3223 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3224 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3226 DrawPreviewLevelLabelExt(label_state);
3230 void DrawPreviewLevelInitial()
3232 DrawPreviewLevelExt(TRUE);
3235 void DrawPreviewLevelAnimation()
3237 DrawPreviewLevelExt(FALSE);
3240 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3241 int graphic, int sync_frame,
3244 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3246 if (mask_mode == USE_MASKING)
3247 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3249 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3252 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3253 int graphic, int sync_frame, int mask_mode)
3255 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3257 if (mask_mode == USE_MASKING)
3258 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3260 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3263 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3265 int lx = LEVELX(x), ly = LEVELY(y);
3267 if (!IN_SCR_FIELD(x, y))
3270 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3271 graphic, GfxFrame[lx][ly], NO_MASKING);
3273 MarkTileDirty(x, y);
3276 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3278 int lx = LEVELX(x), ly = LEVELY(y);
3280 if (!IN_SCR_FIELD(x, y))
3283 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3284 graphic, GfxFrame[lx][ly], NO_MASKING);
3285 MarkTileDirty(x, y);
3288 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3290 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3293 void DrawLevelElementAnimation(int x, int y, int element)
3295 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3297 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3300 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3302 int sx = SCREENX(x), sy = SCREENY(y);
3304 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3307 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3310 DrawGraphicAnimation(sx, sy, graphic);
3313 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3314 DrawLevelFieldCrumbled(x, y);
3316 if (GFX_CRUMBLED(Feld[x][y]))
3317 DrawLevelFieldCrumbled(x, y);
3321 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3323 int sx = SCREENX(x), sy = SCREENY(y);
3326 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3329 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3331 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3334 DrawGraphicAnimation(sx, sy, graphic);
3336 if (GFX_CRUMBLED(element))
3337 DrawLevelFieldCrumbled(x, y);
3340 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3342 if (player->use_murphy)
3344 /* this works only because currently only one player can be "murphy" ... */
3345 static int last_horizontal_dir = MV_LEFT;
3346 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3348 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3349 last_horizontal_dir = move_dir;
3351 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3353 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3355 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3361 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3364 static boolean equalGraphics(int graphic1, int graphic2)
3366 struct GraphicInfo *g1 = &graphic_info[graphic1];
3367 struct GraphicInfo *g2 = &graphic_info[graphic2];
3369 return (g1->bitmap == g2->bitmap &&
3370 g1->src_x == g2->src_x &&
3371 g1->src_y == g2->src_y &&
3372 g1->anim_frames == g2->anim_frames &&
3373 g1->anim_delay == g2->anim_delay &&
3374 g1->anim_mode == g2->anim_mode);
3377 void DrawAllPlayers()
3381 for (i = 0; i < MAX_PLAYERS; i++)
3382 if (stored_player[i].active)
3383 DrawPlayer(&stored_player[i]);
3386 void DrawPlayerField(int x, int y)
3388 if (!IS_PLAYER(x, y))
3391 DrawPlayer(PLAYERINFO(x, y));
3394 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3396 void DrawPlayer(struct PlayerInfo *player)
3398 int jx = player->jx;
3399 int jy = player->jy;
3400 int move_dir = player->MovDir;
3401 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3402 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3403 int last_jx = (player->is_moving ? jx - dx : jx);
3404 int last_jy = (player->is_moving ? jy - dy : jy);
3405 int next_jx = jx + dx;
3406 int next_jy = jy + dy;
3407 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3408 boolean player_is_opaque = FALSE;
3409 int sx = SCREENX(jx), sy = SCREENY(jy);
3410 int sxx = 0, syy = 0;
3411 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3413 int action = ACTION_DEFAULT;
3414 int last_player_graphic = getPlayerGraphic(player, move_dir);
3415 int last_player_frame = player->Frame;
3418 /* GfxElement[][] is set to the element the player is digging or collecting;
3419 remove also for off-screen player if the player is not moving anymore */
3420 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3421 GfxElement[jx][jy] = EL_UNDEFINED;
3423 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3427 if (!IN_LEV_FIELD(jx, jy))
3429 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3430 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3431 printf("DrawPlayerField(): This should never happen!\n");
3436 if (element == EL_EXPLOSION)
3439 action = (player->is_pushing ? ACTION_PUSHING :
3440 player->is_digging ? ACTION_DIGGING :
3441 player->is_collecting ? ACTION_COLLECTING :
3442 player->is_moving ? ACTION_MOVING :
3443 player->is_snapping ? ACTION_SNAPPING :
3444 player->is_dropping ? ACTION_DROPPING :
3445 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3447 if (player->is_waiting)
3448 move_dir = player->dir_waiting;
3450 InitPlayerGfxAnimation(player, action, move_dir);
3452 /* ----------------------------------------------------------------------- */
3453 /* draw things in the field the player is leaving, if needed */
3454 /* ----------------------------------------------------------------------- */
3456 if (player->is_moving)
3458 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3460 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3462 if (last_element == EL_DYNAMITE_ACTIVE ||
3463 last_element == EL_EM_DYNAMITE_ACTIVE ||
3464 last_element == EL_SP_DISK_RED_ACTIVE)
3465 DrawDynamite(last_jx, last_jy);
3467 DrawLevelFieldThruMask(last_jx, last_jy);
3469 else if (last_element == EL_DYNAMITE_ACTIVE ||
3470 last_element == EL_EM_DYNAMITE_ACTIVE ||
3471 last_element == EL_SP_DISK_RED_ACTIVE)
3472 DrawDynamite(last_jx, last_jy);
3474 /* !!! this is not enough to prevent flickering of players which are
3475 moving next to each others without a free tile between them -- this
3476 can only be solved by drawing all players layer by layer (first the
3477 background, then the foreground etc.) !!! => TODO */
3478 else if (!IS_PLAYER(last_jx, last_jy))
3479 DrawLevelField(last_jx, last_jy);
3482 DrawLevelField(last_jx, last_jy);
3485 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3486 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3489 if (!IN_SCR_FIELD(sx, sy))
3492 /* ----------------------------------------------------------------------- */
3493 /* draw things behind the player, if needed */
3494 /* ----------------------------------------------------------------------- */
3497 DrawLevelElement(jx, jy, Back[jx][jy]);
3498 else if (IS_ACTIVE_BOMB(element))
3499 DrawLevelElement(jx, jy, EL_EMPTY);
3502 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3504 int old_element = GfxElement[jx][jy];
3505 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3506 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3508 if (GFX_CRUMBLED(old_element))
3509 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3511 DrawGraphic(sx, sy, old_graphic, frame);
3513 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3514 player_is_opaque = TRUE;
3518 GfxElement[jx][jy] = EL_UNDEFINED;
3520 /* make sure that pushed elements are drawn with correct frame rate */
3521 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3523 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3524 GfxFrame[jx][jy] = player->StepFrame;
3526 DrawLevelField(jx, jy);
3530 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3531 /* ----------------------------------------------------------------------- */
3532 /* draw player himself */
3533 /* ----------------------------------------------------------------------- */
3535 graphic = getPlayerGraphic(player, move_dir);
3537 /* in the case of changed player action or direction, prevent the current
3538 animation frame from being restarted for identical animations */
3539 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3540 player->Frame = last_player_frame;
3542 frame = getGraphicAnimationFrame(graphic, player->Frame);
3546 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3547 sxx = player->GfxPos;
3549 syy = player->GfxPos;
3552 if (player_is_opaque)
3553 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3555 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3557 if (SHIELD_ON(player))
3559 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3560 IMG_SHIELD_NORMAL_ACTIVE);
3561 int frame = getGraphicAnimationFrame(graphic, -1);
3563 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3567 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3570 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3571 sxx = player->GfxPos;
3573 syy = player->GfxPos;
3577 /* ----------------------------------------------------------------------- */
3578 /* draw things the player is pushing, if needed */
3579 /* ----------------------------------------------------------------------- */
3581 if (player->is_pushing && player->is_moving)
3583 int px = SCREENX(jx), py = SCREENY(jy);
3584 int pxx = (TILEX - ABS(sxx)) * dx;
3585 int pyy = (TILEY - ABS(syy)) * dy;
3586 int gfx_frame = GfxFrame[jx][jy];
3592 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3594 element = Feld[next_jx][next_jy];
3595 gfx_frame = GfxFrame[next_jx][next_jy];
3598 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3600 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3601 frame = getGraphicAnimationFrame(graphic, sync_frame);
3603 /* draw background element under pushed element (like the Sokoban field) */
3604 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3606 /* this allows transparent pushing animation over non-black background */
3609 DrawLevelElement(jx, jy, Back[jx][jy]);
3611 DrawLevelElement(jx, jy, EL_EMPTY);
3613 if (Back[next_jx][next_jy])
3614 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3616 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3618 else if (Back[next_jx][next_jy])
3619 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3622 /* do not draw (EM style) pushing animation when pushing is finished */
3623 /* (two-tile animations usually do not contain start and end frame) */
3624 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3625 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3627 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3629 /* masked drawing is needed for EMC style (double) movement graphics */
3630 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3631 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3635 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3636 /* ----------------------------------------------------------------------- */
3637 /* draw player himself */
3638 /* ----------------------------------------------------------------------- */
3640 graphic = getPlayerGraphic(player, move_dir);
3642 /* in the case of changed player action or direction, prevent the current
3643 animation frame from being restarted for identical animations */
3644 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3645 player->Frame = last_player_frame;
3647 frame = getGraphicAnimationFrame(graphic, player->Frame);
3651 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3652 sxx = player->GfxPos;
3654 syy = player->GfxPos;
3657 if (player_is_opaque)
3658 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3660 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3662 if (SHIELD_ON(player))
3664 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3665 IMG_SHIELD_NORMAL_ACTIVE);
3666 int frame = getGraphicAnimationFrame(graphic, -1);
3668 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3672 /* ----------------------------------------------------------------------- */
3673 /* draw things in front of player (active dynamite or dynabombs) */
3674 /* ----------------------------------------------------------------------- */
3676 if (IS_ACTIVE_BOMB(element))
3678 graphic = el2img(element);
3679 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3681 if (game.emulation == EMU_SUPAPLEX)
3682 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3684 DrawGraphicThruMask(sx, sy, graphic, frame);
3687 if (player_is_moving && last_element == EL_EXPLOSION)
3689 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3690 GfxElement[last_jx][last_jy] : EL_EMPTY);
3691 int graphic = el_act2img(element, ACTION_EXPLODING);
3692 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3693 int phase = ExplodePhase[last_jx][last_jy] - 1;
3694 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3697 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3700 /* ----------------------------------------------------------------------- */
3701 /* draw elements the player is just walking/passing through/under */
3702 /* ----------------------------------------------------------------------- */
3704 if (player_is_moving)
3706 /* handle the field the player is leaving ... */
3707 if (IS_ACCESSIBLE_INSIDE(last_element))
3708 DrawLevelField(last_jx, last_jy);
3709 else if (IS_ACCESSIBLE_UNDER(last_element))
3710 DrawLevelFieldThruMask(last_jx, last_jy);
3713 /* do not redraw accessible elements if the player is just pushing them */
3714 if (!player_is_moving || !player->is_pushing)
3716 /* ... and the field the player is entering */
3717 if (IS_ACCESSIBLE_INSIDE(element))
3718 DrawLevelField(jx, jy);
3719 else if (IS_ACCESSIBLE_UNDER(element))
3720 DrawLevelFieldThruMask(jx, jy);
3723 MarkTileDirty(sx, sy);
3726 /* ------------------------------------------------------------------------- */
3728 void WaitForEventToContinue()
3730 boolean still_wait = TRUE;
3732 /* simulate releasing mouse button over last gadget, if still pressed */
3734 HandleGadgets(-1, -1, 0);
3736 button_status = MB_RELEASED;
3750 case EVENT_BUTTONPRESS:
3751 case EVENT_KEYPRESS:
3755 case EVENT_KEYRELEASE:
3756 ClearPlayerAction();
3760 HandleOtherEvents(&event);
3764 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3773 #define MAX_REQUEST_LINES 13
3774 #define MAX_REQUEST_LINE_FONT1_LEN 7
3775 #define MAX_REQUEST_LINE_FONT2_LEN 10
3777 static int RequestHandleEvents(unsigned int req_state)
3779 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3780 local_player->LevelSolved_GameEnd);
3781 int width = request.width;
3782 int height = request.height;
3786 setRequestPosition(&sx, &sy, FALSE);
3788 button_status = MB_RELEASED;
3790 request_gadget_id = -1;
3797 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3799 HandleGameActions();
3801 SetDrawtoField(DRAW_TO_BACKBUFFER);
3803 if (global.use_envelope_request)
3805 /* copy current state of request area to middle of playfield area */
3806 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3814 while (NextValidEvent(&event))
3818 case EVENT_BUTTONPRESS:
3819 case EVENT_BUTTONRELEASE:
3820 case EVENT_MOTIONNOTIFY:
3824 if (event.type == EVENT_MOTIONNOTIFY)
3829 motion_status = TRUE;
3830 mx = ((MotionEvent *) &event)->x;
3831 my = ((MotionEvent *) &event)->y;
3835 motion_status = FALSE;
3836 mx = ((ButtonEvent *) &event)->x;
3837 my = ((ButtonEvent *) &event)->y;
3838 if (event.type == EVENT_BUTTONPRESS)
3839 button_status = ((ButtonEvent *) &event)->button;
3841 button_status = MB_RELEASED;
3844 /* this sets 'request_gadget_id' */
3845 HandleGadgets(mx, my, button_status);
3847 switch (request_gadget_id)
3849 case TOOL_CTRL_ID_YES:
3852 case TOOL_CTRL_ID_NO:
3855 case TOOL_CTRL_ID_CONFIRM:
3856 result = TRUE | FALSE;
3859 case TOOL_CTRL_ID_PLAYER_1:
3862 case TOOL_CTRL_ID_PLAYER_2:
3865 case TOOL_CTRL_ID_PLAYER_3:
3868 case TOOL_CTRL_ID_PLAYER_4:
3879 case EVENT_KEYPRESS:
3881 Key key = GetEventKey((KeyEvent *)&event, TRUE);
3886 if (req_state & REQ_CONFIRM)
3891 #if defined(TARGET_SDL2)
3898 #if defined(TARGET_SDL2)
3905 HandleKeysDebug(key);
3909 if (req_state & REQ_PLAYER)
3915 case EVENT_KEYRELEASE:
3916 ClearPlayerAction();
3920 HandleOtherEvents(&event);
3925 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3927 int joy = AnyJoystick();
3929 if (joy & JOY_BUTTON_1)
3931 else if (joy & JOY_BUTTON_2)
3937 if (global.use_envelope_request)
3939 /* copy back current state of pressed buttons inside request area */
3940 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
3950 static boolean RequestDoor(char *text, unsigned int req_state)
3952 unsigned int old_door_state;
3953 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3954 int font_nr = FONT_TEXT_2;
3959 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3961 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3962 font_nr = FONT_TEXT_1;
3965 if (game_status == GAME_MODE_PLAYING)
3966 BlitScreenToBitmap(backbuffer);
3968 /* disable deactivated drawing when quick-loading level tape recording */
3969 if (tape.playing && tape.deactivate_display)
3970 TapeDeactivateDisplayOff(TRUE);
3972 SetMouseCursor(CURSOR_DEFAULT);
3974 #if defined(NETWORK_AVALIABLE)
3975 /* pause network game while waiting for request to answer */
3976 if (options.network &&
3977 game_status == GAME_MODE_PLAYING &&
3978 req_state & REQUEST_WAIT_FOR_INPUT)
3979 SendToServer_PausePlaying();
3982 old_door_state = GetDoorState();
3984 /* simulate releasing mouse button over last gadget, if still pressed */
3986 HandleGadgets(-1, -1, 0);
3990 /* draw released gadget before proceeding */
3993 if (old_door_state & DOOR_OPEN_1)
3995 CloseDoor(DOOR_CLOSE_1);
3997 /* save old door content */
3998 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3999 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4002 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4003 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4005 /* clear door drawing field */
4006 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4008 /* force DOOR font inside door area */
4009 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4011 /* write text for request */
4012 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4014 char text_line[max_request_line_len + 1];
4020 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4022 tc = *(text_ptr + tx);
4023 // if (!tc || tc == ' ')
4024 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4028 if ((tc == '?' || tc == '!') && tl == 0)
4038 strncpy(text_line, text_ptr, tl);
4041 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4042 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4043 text_line, font_nr);
4045 text_ptr += tl + (tc == ' ' ? 1 : 0);
4046 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4051 if (req_state & REQ_ASK)
4053 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4054 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4056 else if (req_state & REQ_CONFIRM)
4058 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4060 else if (req_state & REQ_PLAYER)
4062 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4063 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4064 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4065 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4068 /* copy request gadgets to door backbuffer */
4069 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4071 OpenDoor(DOOR_OPEN_1);
4073 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4075 if (game_status == GAME_MODE_PLAYING)
4077 SetPanelBackground();
4078 SetDrawBackgroundMask(REDRAW_DOOR_1);
4082 SetDrawBackgroundMask(REDRAW_FIELD);
4088 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4090 // ---------- handle request buttons ----------
4091 result = RequestHandleEvents(req_state);
4095 if (!(req_state & REQ_STAY_OPEN))
4097 CloseDoor(DOOR_CLOSE_1);
4099 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4100 (req_state & REQ_REOPEN))
4101 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4106 if (game_status == GAME_MODE_PLAYING)
4108 SetPanelBackground();
4109 SetDrawBackgroundMask(REDRAW_DOOR_1);
4113 SetDrawBackgroundMask(REDRAW_FIELD);
4116 #if defined(NETWORK_AVALIABLE)
4117 /* continue network game after request */
4118 if (options.network &&
4119 game_status == GAME_MODE_PLAYING &&
4120 req_state & REQUEST_WAIT_FOR_INPUT)
4121 SendToServer_ContinuePlaying();
4124 /* restore deactivated drawing when quick-loading level tape recording */
4125 if (tape.playing && tape.deactivate_display)
4126 TapeDeactivateDisplayOn();
4131 static boolean RequestEnvelope(char *text, unsigned int req_state)
4135 if (game_status == GAME_MODE_PLAYING)
4136 BlitScreenToBitmap(backbuffer);
4138 /* disable deactivated drawing when quick-loading level tape recording */
4139 if (tape.playing && tape.deactivate_display)
4140 TapeDeactivateDisplayOff(TRUE);
4142 SetMouseCursor(CURSOR_DEFAULT);
4144 #if defined(NETWORK_AVALIABLE)
4145 /* pause network game while waiting for request to answer */
4146 if (options.network &&
4147 game_status == GAME_MODE_PLAYING &&
4148 req_state & REQUEST_WAIT_FOR_INPUT)
4149 SendToServer_PausePlaying();
4152 /* simulate releasing mouse button over last gadget, if still pressed */
4154 HandleGadgets(-1, -1, 0);
4158 // (replace with setting corresponding request background)
4159 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4160 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4162 /* clear door drawing field */
4163 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4165 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4167 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4169 if (game_status == GAME_MODE_PLAYING)
4171 SetPanelBackground();
4172 SetDrawBackgroundMask(REDRAW_DOOR_1);
4176 SetDrawBackgroundMask(REDRAW_FIELD);
4182 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4184 // ---------- handle request buttons ----------
4185 result = RequestHandleEvents(req_state);
4189 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4193 if (game_status == GAME_MODE_PLAYING)
4195 SetPanelBackground();
4196 SetDrawBackgroundMask(REDRAW_DOOR_1);
4200 SetDrawBackgroundMask(REDRAW_FIELD);
4203 #if defined(NETWORK_AVALIABLE)
4204 /* continue network game after request */
4205 if (options.network &&
4206 game_status == GAME_MODE_PLAYING &&
4207 req_state & REQUEST_WAIT_FOR_INPUT)
4208 SendToServer_ContinuePlaying();
4211 /* restore deactivated drawing when quick-loading level tape recording */
4212 if (tape.playing && tape.deactivate_display)
4213 TapeDeactivateDisplayOn();
4218 boolean Request(char *text, unsigned int req_state)
4220 boolean overlay_active = GetOverlayActive();
4223 SetOverlayActive(FALSE);
4225 if (global.use_envelope_request)
4226 result = RequestEnvelope(text, req_state);
4228 result = RequestDoor(text, req_state);
4230 SetOverlayActive(overlay_active);
4235 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4237 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4238 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4241 if (dpo1->sort_priority != dpo2->sort_priority)
4242 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4244 compare_result = dpo1->nr - dpo2->nr;
4246 return compare_result;
4249 void InitGraphicCompatibilityInfo_Doors()
4255 struct DoorInfo *door;
4259 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4260 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4262 { -1, -1, -1, NULL }
4264 struct Rect door_rect_list[] =
4266 { DX, DY, DXSIZE, DYSIZE },
4267 { VX, VY, VXSIZE, VYSIZE }
4271 for (i = 0; doors[i].door_token != -1; i++)
4273 int door_token = doors[i].door_token;
4274 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4275 int part_1 = doors[i].part_1;
4276 int part_8 = doors[i].part_8;
4277 int part_2 = part_1 + 1;
4278 int part_3 = part_1 + 2;
4279 struct DoorInfo *door = doors[i].door;
4280 struct Rect *door_rect = &door_rect_list[door_index];
4281 boolean door_gfx_redefined = FALSE;
4283 /* check if any door part graphic definitions have been redefined */
4285 for (j = 0; door_part_controls[j].door_token != -1; j++)
4287 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4288 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4290 if (dpc->door_token == door_token && fi->redefined)
4291 door_gfx_redefined = TRUE;
4294 /* check for old-style door graphic/animation modifications */
4296 if (!door_gfx_redefined)
4298 if (door->anim_mode & ANIM_STATIC_PANEL)
4300 door->panel.step_xoffset = 0;
4301 door->panel.step_yoffset = 0;
4304 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4306 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4307 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4308 int num_door_steps, num_panel_steps;
4310 /* remove door part graphics other than the two default wings */
4312 for (j = 0; door_part_controls[j].door_token != -1; j++)
4314 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4315 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4317 if (dpc->graphic >= part_3 &&
4318 dpc->graphic <= part_8)
4322 /* set graphics and screen positions of the default wings */
4324 g_part_1->width = door_rect->width;
4325 g_part_1->height = door_rect->height;
4326 g_part_2->width = door_rect->width;
4327 g_part_2->height = door_rect->height;
4328 g_part_2->src_x = door_rect->width;
4329 g_part_2->src_y = g_part_1->src_y;
4331 door->part_2.x = door->part_1.x;
4332 door->part_2.y = door->part_1.y;
4334 if (door->width != -1)
4336 g_part_1->width = door->width;
4337 g_part_2->width = door->width;
4339 // special treatment for graphics and screen position of right wing
4340 g_part_2->src_x += door_rect->width - door->width;
4341 door->part_2.x += door_rect->width - door->width;
4344 if (door->height != -1)
4346 g_part_1->height = door->height;
4347 g_part_2->height = door->height;
4349 // special treatment for graphics and screen position of bottom wing
4350 g_part_2->src_y += door_rect->height - door->height;
4351 door->part_2.y += door_rect->height - door->height;
4354 /* set animation delays for the default wings and panels */
4356 door->part_1.step_delay = door->step_delay;
4357 door->part_2.step_delay = door->step_delay;
4358 door->panel.step_delay = door->step_delay;
4360 /* set animation draw order for the default wings */
4362 door->part_1.sort_priority = 2; /* draw left wing over ... */
4363 door->part_2.sort_priority = 1; /* ... right wing */
4365 /* set animation draw offset for the default wings */
4367 if (door->anim_mode & ANIM_HORIZONTAL)
4369 door->part_1.step_xoffset = door->step_offset;
4370 door->part_1.step_yoffset = 0;
4371 door->part_2.step_xoffset = door->step_offset * -1;
4372 door->part_2.step_yoffset = 0;
4374 num_door_steps = g_part_1->width / door->step_offset;
4376 else // ANIM_VERTICAL
4378 door->part_1.step_xoffset = 0;
4379 door->part_1.step_yoffset = door->step_offset;
4380 door->part_2.step_xoffset = 0;
4381 door->part_2.step_yoffset = door->step_offset * -1;
4383 num_door_steps = g_part_1->height / door->step_offset;
4386 /* set animation draw offset for the default panels */
4388 if (door->step_offset > 1)
4390 num_panel_steps = 2 * door_rect->height / door->step_offset;
4391 door->panel.start_step = num_panel_steps - num_door_steps;
4392 door->panel.start_step_closing = door->panel.start_step;
4396 num_panel_steps = door_rect->height / door->step_offset;
4397 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4398 door->panel.start_step_closing = door->panel.start_step;
4399 door->panel.step_delay *= 2;
4410 for (i = 0; door_part_controls[i].door_token != -1; i++)
4412 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4413 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4415 /* initialize "start_step_opening" and "start_step_closing", if needed */
4416 if (dpc->pos->start_step_opening == 0 &&
4417 dpc->pos->start_step_closing == 0)
4419 // dpc->pos->start_step_opening = dpc->pos->start_step;
4420 dpc->pos->start_step_closing = dpc->pos->start_step;
4423 /* fill structure for door part draw order (sorted below) */
4425 dpo->sort_priority = dpc->pos->sort_priority;
4428 /* sort door part controls according to sort_priority and graphic number */
4429 qsort(door_part_order, MAX_DOOR_PARTS,
4430 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4433 unsigned int OpenDoor(unsigned int door_state)
4435 if (door_state & DOOR_COPY_BACK)
4437 if (door_state & DOOR_OPEN_1)
4438 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4439 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4441 if (door_state & DOOR_OPEN_2)
4442 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4443 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4445 door_state &= ~DOOR_COPY_BACK;
4448 return MoveDoor(door_state);
4451 unsigned int CloseDoor(unsigned int door_state)
4453 unsigned int old_door_state = GetDoorState();
4455 if (!(door_state & DOOR_NO_COPY_BACK))
4457 if (old_door_state & DOOR_OPEN_1)
4458 BlitBitmap(backbuffer, bitmap_db_door_1,
4459 DX, DY, DXSIZE, DYSIZE, 0, 0);
4461 if (old_door_state & DOOR_OPEN_2)
4462 BlitBitmap(backbuffer, bitmap_db_door_2,
4463 VX, VY, VXSIZE, VYSIZE, 0, 0);
4465 door_state &= ~DOOR_NO_COPY_BACK;
4468 return MoveDoor(door_state);
4471 unsigned int GetDoorState()
4473 return MoveDoor(DOOR_GET_STATE);
4476 unsigned int SetDoorState(unsigned int door_state)
4478 return MoveDoor(door_state | DOOR_SET_STATE);
4481 int euclid(int a, int b)
4483 return (b ? euclid(b, a % b) : a);
4486 unsigned int MoveDoor(unsigned int door_state)
4488 struct Rect door_rect_list[] =
4490 { DX, DY, DXSIZE, DYSIZE },
4491 { VX, VY, VXSIZE, VYSIZE }
4493 static int door1 = DOOR_CLOSE_1;
4494 static int door2 = DOOR_CLOSE_2;
4495 unsigned int door_delay = 0;
4496 unsigned int door_delay_value;
4499 if (door_state == DOOR_GET_STATE)
4500 return (door1 | door2);
4502 if (door_state & DOOR_SET_STATE)
4504 if (door_state & DOOR_ACTION_1)
4505 door1 = door_state & DOOR_ACTION_1;
4506 if (door_state & DOOR_ACTION_2)
4507 door2 = door_state & DOOR_ACTION_2;
4509 return (door1 | door2);
4512 if (!(door_state & DOOR_FORCE_REDRAW))
4514 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4515 door_state &= ~DOOR_OPEN_1;
4516 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4517 door_state &= ~DOOR_CLOSE_1;
4518 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4519 door_state &= ~DOOR_OPEN_2;
4520 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4521 door_state &= ~DOOR_CLOSE_2;
4524 if (global.autoplay_leveldir)
4526 door_state |= DOOR_NO_DELAY;
4527 door_state &= ~DOOR_CLOSE_ALL;
4530 if (game_status == GAME_MODE_EDITOR)
4531 door_state |= DOOR_NO_DELAY;
4533 if (door_state & DOOR_ACTION)
4535 boolean door_panel_drawn[NUM_DOORS];
4536 boolean panel_has_doors[NUM_DOORS];
4537 boolean door_part_skip[MAX_DOOR_PARTS];
4538 boolean door_part_done[MAX_DOOR_PARTS];
4539 boolean door_part_done_all;
4540 int num_steps[MAX_DOOR_PARTS];
4541 int max_move_delay = 0; // delay for complete animations of all doors
4542 int max_step_delay = 0; // delay (ms) between two animation frames
4543 int num_move_steps = 0; // number of animation steps for all doors
4544 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4545 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4546 int current_move_delay = 0;
4550 for (i = 0; i < NUM_DOORS; i++)
4551 panel_has_doors[i] = FALSE;
4553 for (i = 0; i < MAX_DOOR_PARTS; i++)
4555 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4556 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4557 int door_token = dpc->door_token;
4559 door_part_done[i] = FALSE;
4560 door_part_skip[i] = (!(door_state & door_token) ||
4564 for (i = 0; i < MAX_DOOR_PARTS; i++)
4566 int nr = door_part_order[i].nr;
4567 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4568 struct DoorPartPosInfo *pos = dpc->pos;
4569 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4570 int door_token = dpc->door_token;
4571 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4572 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4573 int step_xoffset = ABS(pos->step_xoffset);
4574 int step_yoffset = ABS(pos->step_yoffset);
4575 int step_delay = pos->step_delay;
4576 int current_door_state = door_state & door_token;
4577 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4578 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4579 boolean part_opening = (is_panel ? door_closing : door_opening);
4580 int start_step = (part_opening ? pos->start_step_opening :
4581 pos->start_step_closing);
4582 float move_xsize = (step_xoffset ? g->width : 0);
4583 float move_ysize = (step_yoffset ? g->height : 0);
4584 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4585 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4586 int move_steps = (move_xsteps && move_ysteps ?
4587 MIN(move_xsteps, move_ysteps) :
4588 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4589 int move_delay = move_steps * step_delay;
4591 if (door_part_skip[nr])
4594 max_move_delay = MAX(max_move_delay, move_delay);
4595 max_step_delay = (max_step_delay == 0 ? step_delay :
4596 euclid(max_step_delay, step_delay));
4597 num_steps[nr] = move_steps;
4601 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4603 panel_has_doors[door_index] = TRUE;
4607 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4609 num_move_steps = max_move_delay / max_step_delay;
4610 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4612 door_delay_value = max_step_delay;
4614 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4616 start = num_move_steps - 1;
4620 /* opening door sound has priority over simultaneously closing door */
4621 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4622 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4623 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4624 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4627 for (k = start; k < num_move_steps; k++)
4629 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4631 door_part_done_all = TRUE;
4633 for (i = 0; i < NUM_DOORS; i++)
4634 door_panel_drawn[i] = FALSE;
4636 for (i = 0; i < MAX_DOOR_PARTS; i++)
4638 int nr = door_part_order[i].nr;
4639 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4640 struct DoorPartPosInfo *pos = dpc->pos;
4641 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4642 int door_token = dpc->door_token;
4643 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4644 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4645 boolean is_panel_and_door_has_closed = FALSE;
4646 struct Rect *door_rect = &door_rect_list[door_index];
4647 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4649 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4650 int current_door_state = door_state & door_token;
4651 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4652 boolean door_closing = !door_opening;
4653 boolean part_opening = (is_panel ? door_closing : door_opening);
4654 boolean part_closing = !part_opening;
4655 int start_step = (part_opening ? pos->start_step_opening :
4656 pos->start_step_closing);
4657 int step_delay = pos->step_delay;
4658 int step_factor = step_delay / max_step_delay;
4659 int k1 = (step_factor ? k / step_factor + 1 : k);
4660 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4661 int kk = MAX(0, k2);
4664 int src_x, src_y, src_xx, src_yy;
4665 int dst_x, dst_y, dst_xx, dst_yy;
4668 if (door_part_skip[nr])
4671 if (!(door_state & door_token))
4679 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4680 int kk_door = MAX(0, k2_door);
4681 int sync_frame = kk_door * door_delay_value;
4682 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4684 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4685 &g_src_x, &g_src_y);
4690 if (!door_panel_drawn[door_index])
4692 ClearRectangle(drawto, door_rect->x, door_rect->y,
4693 door_rect->width, door_rect->height);
4695 door_panel_drawn[door_index] = TRUE;
4698 // draw opening or closing door parts
4700 if (pos->step_xoffset < 0) // door part on right side
4703 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4706 if (dst_xx + width > door_rect->width)
4707 width = door_rect->width - dst_xx;
4709 else // door part on left side
4712 dst_xx = pos->x - kk * pos->step_xoffset;
4716 src_xx = ABS(dst_xx);
4720 width = g->width - src_xx;
4722 if (width > door_rect->width)
4723 width = door_rect->width;
4725 // printf("::: k == %d [%d] \n", k, start_step);
4728 if (pos->step_yoffset < 0) // door part on bottom side
4731 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4734 if (dst_yy + height > door_rect->height)
4735 height = door_rect->height - dst_yy;
4737 else // door part on top side
4740 dst_yy = pos->y - kk * pos->step_yoffset;
4744 src_yy = ABS(dst_yy);
4748 height = g->height - src_yy;
4751 src_x = g_src_x + src_xx;
4752 src_y = g_src_y + src_yy;
4754 dst_x = door_rect->x + dst_xx;
4755 dst_y = door_rect->y + dst_yy;
4757 is_panel_and_door_has_closed =
4760 panel_has_doors[door_index] &&
4761 k >= num_move_steps_doors_only - 1);
4763 if (width >= 0 && width <= g->width &&
4764 height >= 0 && height <= g->height &&
4765 !is_panel_and_door_has_closed)
4767 if (is_panel || !pos->draw_masked)
4768 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4771 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4775 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4777 if ((part_opening && (width < 0 || height < 0)) ||
4778 (part_closing && (width >= g->width && height >= g->height)))
4779 door_part_done[nr] = TRUE;
4781 // continue door part animations, but not panel after door has closed
4782 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4783 door_part_done_all = FALSE;
4786 if (!(door_state & DOOR_NO_DELAY))
4790 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4792 current_move_delay += max_step_delay;
4795 if (door_part_done_all)
4800 if (door_state & DOOR_ACTION_1)
4801 door1 = door_state & DOOR_ACTION_1;
4802 if (door_state & DOOR_ACTION_2)
4803 door2 = door_state & DOOR_ACTION_2;
4805 // draw masked border over door area
4806 DrawMaskedBorder(REDRAW_DOOR_1);
4807 DrawMaskedBorder(REDRAW_DOOR_2);
4809 return (door1 | door2);
4812 static boolean useSpecialEditorDoor()
4814 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4815 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4817 // do not draw special editor door if editor border defined or redefined
4818 if (graphic_info[graphic].bitmap != NULL || redefined)
4821 // do not draw special editor door if global border defined to be empty
4822 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4825 // do not draw special editor door if viewport definitions do not match
4829 EY + EYSIZE != VY + VYSIZE)
4835 void DrawSpecialEditorDoor()
4837 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4838 int top_border_width = gfx1->width;
4839 int top_border_height = gfx1->height;
4840 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4841 int ex = EX - outer_border;
4842 int ey = EY - outer_border;
4843 int vy = VY - outer_border;
4844 int exsize = EXSIZE + 2 * outer_border;
4846 if (!useSpecialEditorDoor())
4849 /* draw bigger level editor toolbox window */
4850 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4851 top_border_width, top_border_height, ex, ey - top_border_height);
4852 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4853 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4855 redraw_mask |= REDRAW_ALL;
4858 void UndrawSpecialEditorDoor()
4860 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4861 int top_border_width = gfx1->width;
4862 int top_border_height = gfx1->height;
4863 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4864 int ex = EX - outer_border;
4865 int ey = EY - outer_border;
4866 int ey_top = ey - top_border_height;
4867 int exsize = EXSIZE + 2 * outer_border;
4868 int eysize = EYSIZE + 2 * outer_border;
4870 if (!useSpecialEditorDoor())
4873 /* draw normal tape recorder window */
4874 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4876 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4877 ex, ey_top, top_border_width, top_border_height,
4879 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4880 ex, ey, exsize, eysize, ex, ey);
4884 // if screen background is set to "[NONE]", clear editor toolbox window
4885 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4886 ClearRectangle(drawto, ex, ey, exsize, eysize);
4889 redraw_mask |= REDRAW_ALL;
4893 /* ---------- new tool button stuff ---------------------------------------- */
4898 struct TextPosInfo *pos;
4901 } toolbutton_info[NUM_TOOL_BUTTONS] =
4904 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
4905 TOOL_CTRL_ID_YES, "yes"
4908 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
4909 TOOL_CTRL_ID_NO, "no"
4912 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
4913 TOOL_CTRL_ID_CONFIRM, "confirm"
4916 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
4917 TOOL_CTRL_ID_PLAYER_1, "player 1"
4920 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
4921 TOOL_CTRL_ID_PLAYER_2, "player 2"
4924 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
4925 TOOL_CTRL_ID_PLAYER_3, "player 3"
4928 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
4929 TOOL_CTRL_ID_PLAYER_4, "player 4"
4933 void CreateToolButtons()
4937 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4939 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4940 struct TextPosInfo *pos = toolbutton_info[i].pos;
4941 struct GadgetInfo *gi;
4942 Bitmap *deco_bitmap = None;
4943 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4944 unsigned int event_mask = GD_EVENT_RELEASED;
4947 int gd_x = gfx->src_x;
4948 int gd_y = gfx->src_y;
4949 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4950 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4953 if (global.use_envelope_request)
4954 setRequestPosition(&dx, &dy, TRUE);
4956 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4958 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4960 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4961 pos->size, &deco_bitmap, &deco_x, &deco_y);
4962 deco_xpos = (gfx->width - pos->size) / 2;
4963 deco_ypos = (gfx->height - pos->size) / 2;
4966 gi = CreateGadget(GDI_CUSTOM_ID, id,
4967 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4968 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4969 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4970 GDI_WIDTH, gfx->width,
4971 GDI_HEIGHT, gfx->height,
4972 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4973 GDI_STATE, GD_BUTTON_UNPRESSED,
4974 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4975 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4976 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4977 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4978 GDI_DECORATION_SIZE, pos->size, pos->size,
4979 GDI_DECORATION_SHIFTING, 1, 1,
4980 GDI_DIRECT_DRAW, FALSE,
4981 GDI_EVENT_MASK, event_mask,
4982 GDI_CALLBACK_ACTION, HandleToolButtons,
4986 Error(ERR_EXIT, "cannot create gadget");
4988 tool_gadget[id] = gi;
4992 void FreeToolButtons()
4996 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4997 FreeGadget(tool_gadget[i]);
5000 static void UnmapToolButtons()
5004 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5005 UnmapGadget(tool_gadget[i]);
5008 static void HandleToolButtons(struct GadgetInfo *gi)
5010 request_gadget_id = gi->custom_id;
5013 static struct Mapping_EM_to_RND_object
5016 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5017 boolean is_backside; /* backside of moving element */
5023 em_object_mapping_list[] =
5026 Xblank, TRUE, FALSE,
5030 Yacid_splash_eB, FALSE, FALSE,
5031 EL_ACID_SPLASH_RIGHT, -1, -1
5034 Yacid_splash_wB, FALSE, FALSE,
5035 EL_ACID_SPLASH_LEFT, -1, -1
5038 #ifdef EM_ENGINE_BAD_ROLL
5040 Xstone_force_e, FALSE, FALSE,
5041 EL_ROCK, -1, MV_BIT_RIGHT
5044 Xstone_force_w, FALSE, FALSE,
5045 EL_ROCK, -1, MV_BIT_LEFT
5048 Xnut_force_e, FALSE, FALSE,
5049 EL_NUT, -1, MV_BIT_RIGHT
5052 Xnut_force_w, FALSE, FALSE,
5053 EL_NUT, -1, MV_BIT_LEFT
5056 Xspring_force_e, FALSE, FALSE,
5057 EL_SPRING, -1, MV_BIT_RIGHT
5060 Xspring_force_w, FALSE, FALSE,
5061 EL_SPRING, -1, MV_BIT_LEFT
5064 Xemerald_force_e, FALSE, FALSE,
5065 EL_EMERALD, -1, MV_BIT_RIGHT
5068 Xemerald_force_w, FALSE, FALSE,
5069 EL_EMERALD, -1, MV_BIT_LEFT
5072 Xdiamond_force_e, FALSE, FALSE,
5073 EL_DIAMOND, -1, MV_BIT_RIGHT
5076 Xdiamond_force_w, FALSE, FALSE,
5077 EL_DIAMOND, -1, MV_BIT_LEFT
5080 Xbomb_force_e, FALSE, FALSE,
5081 EL_BOMB, -1, MV_BIT_RIGHT
5084 Xbomb_force_w, FALSE, FALSE,
5085 EL_BOMB, -1, MV_BIT_LEFT
5087 #endif /* EM_ENGINE_BAD_ROLL */
5090 Xstone, TRUE, FALSE,
5094 Xstone_pause, FALSE, FALSE,
5098 Xstone_fall, FALSE, FALSE,
5102 Ystone_s, FALSE, FALSE,
5103 EL_ROCK, ACTION_FALLING, -1
5106 Ystone_sB, FALSE, TRUE,
5107 EL_ROCK, ACTION_FALLING, -1
5110 Ystone_e, FALSE, FALSE,
5111 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5114 Ystone_eB, FALSE, TRUE,
5115 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5118 Ystone_w, FALSE, FALSE,
5119 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5122 Ystone_wB, FALSE, TRUE,
5123 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5130 Xnut_pause, FALSE, FALSE,
5134 Xnut_fall, FALSE, FALSE,
5138 Ynut_s, FALSE, FALSE,
5139 EL_NUT, ACTION_FALLING, -1
5142 Ynut_sB, FALSE, TRUE,
5143 EL_NUT, ACTION_FALLING, -1
5146 Ynut_e, FALSE, FALSE,
5147 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5150 Ynut_eB, FALSE, TRUE,
5151 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5154 Ynut_w, FALSE, FALSE,
5155 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5158 Ynut_wB, FALSE, TRUE,
5159 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5162 Xbug_n, TRUE, FALSE,
5166 Xbug_e, TRUE, FALSE,
5167 EL_BUG_RIGHT, -1, -1
5170 Xbug_s, TRUE, FALSE,
5174 Xbug_w, TRUE, FALSE,
5178 Xbug_gon, FALSE, FALSE,
5182 Xbug_goe, FALSE, FALSE,
5183 EL_BUG_RIGHT, -1, -1
5186 Xbug_gos, FALSE, FALSE,
5190 Xbug_gow, FALSE, FALSE,
5194 Ybug_n, FALSE, FALSE,
5195 EL_BUG, ACTION_MOVING, MV_BIT_UP
5198 Ybug_nB, FALSE, TRUE,
5199 EL_BUG, ACTION_MOVING, MV_BIT_UP
5202 Ybug_e, FALSE, FALSE,
5203 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5206 Ybug_eB, FALSE, TRUE,
5207 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5210 Ybug_s, FALSE, FALSE,
5211 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5214 Ybug_sB, FALSE, TRUE,
5215 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5218 Ybug_w, FALSE, FALSE,
5219 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5222 Ybug_wB, FALSE, TRUE,
5223 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5226 Ybug_w_n, FALSE, FALSE,
5227 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5230 Ybug_n_e, FALSE, FALSE,
5231 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5234 Ybug_e_s, FALSE, FALSE,
5235 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5238 Ybug_s_w, FALSE, FALSE,
5239 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5242 Ybug_e_n, FALSE, FALSE,
5243 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5246 Ybug_s_e, FALSE, FALSE,
5247 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5250 Ybug_w_s, FALSE, FALSE,
5251 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5254 Ybug_n_w, FALSE, FALSE,
5255 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5258 Ybug_stone, FALSE, FALSE,
5259 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5262 Ybug_spring, FALSE, FALSE,
5263 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5266 Xtank_n, TRUE, FALSE,
5267 EL_SPACESHIP_UP, -1, -1
5270 Xtank_e, TRUE, FALSE,
5271 EL_SPACESHIP_RIGHT, -1, -1
5274 Xtank_s, TRUE, FALSE,
5275 EL_SPACESHIP_DOWN, -1, -1
5278 Xtank_w, TRUE, FALSE,
5279 EL_SPACESHIP_LEFT, -1, -1
5282 Xtank_gon, FALSE, FALSE,
5283 EL_SPACESHIP_UP, -1, -1
5286 Xtank_goe, FALSE, FALSE,
5287 EL_SPACESHIP_RIGHT, -1, -1
5290 Xtank_gos, FALSE, FALSE,
5291 EL_SPACESHIP_DOWN, -1, -1
5294 Xtank_gow, FALSE, FALSE,
5295 EL_SPACESHIP_LEFT, -1, -1
5298 Ytank_n, FALSE, FALSE,
5299 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5302 Ytank_nB, FALSE, TRUE,
5303 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5306 Ytank_e, FALSE, FALSE,
5307 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5310 Ytank_eB, FALSE, TRUE,
5311 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5314 Ytank_s, FALSE, FALSE,
5315 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5318 Ytank_sB, FALSE, TRUE,
5319 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5322 Ytank_w, FALSE, FALSE,
5323 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5326 Ytank_wB, FALSE, TRUE,
5327 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5330 Ytank_w_n, FALSE, FALSE,
5331 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5334 Ytank_n_e, FALSE, FALSE,
5335 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5338 Ytank_e_s, FALSE, FALSE,
5339 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5342 Ytank_s_w, FALSE, FALSE,
5343 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5346 Ytank_e_n, FALSE, FALSE,
5347 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5350 Ytank_s_e, FALSE, FALSE,
5351 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5354 Ytank_w_s, FALSE, FALSE,
5355 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5358 Ytank_n_w, FALSE, FALSE,
5359 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5362 Ytank_stone, FALSE, FALSE,
5363 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5366 Ytank_spring, FALSE, FALSE,
5367 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5370 Xandroid, TRUE, FALSE,
5371 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5374 Xandroid_1_n, FALSE, FALSE,
5375 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5378 Xandroid_2_n, FALSE, FALSE,
5379 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5382 Xandroid_1_e, FALSE, FALSE,
5383 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5386 Xandroid_2_e, FALSE, FALSE,
5387 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5390 Xandroid_1_w, FALSE, FALSE,
5391 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5394 Xandroid_2_w, FALSE, FALSE,
5395 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5398 Xandroid_1_s, FALSE, FALSE,
5399 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5402 Xandroid_2_s, FALSE, FALSE,
5403 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5406 Yandroid_n, FALSE, FALSE,
5407 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5410 Yandroid_nB, FALSE, TRUE,
5411 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5414 Yandroid_ne, FALSE, FALSE,
5415 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5418 Yandroid_neB, FALSE, TRUE,
5419 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5422 Yandroid_e, FALSE, FALSE,
5423 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5426 Yandroid_eB, FALSE, TRUE,
5427 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5430 Yandroid_se, FALSE, FALSE,
5431 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5434 Yandroid_seB, FALSE, TRUE,
5435 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5438 Yandroid_s, FALSE, FALSE,
5439 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5442 Yandroid_sB, FALSE, TRUE,
5443 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5446 Yandroid_sw, FALSE, FALSE,
5447 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5450 Yandroid_swB, FALSE, TRUE,
5451 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5454 Yandroid_w, FALSE, FALSE,
5455 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5458 Yandroid_wB, FALSE, TRUE,
5459 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5462 Yandroid_nw, FALSE, FALSE,
5463 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5466 Yandroid_nwB, FALSE, TRUE,
5467 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5470 Xspring, TRUE, FALSE,
5474 Xspring_pause, FALSE, FALSE,
5478 Xspring_e, FALSE, FALSE,
5482 Xspring_w, FALSE, FALSE,
5486 Xspring_fall, FALSE, FALSE,
5490 Yspring_s, FALSE, FALSE,
5491 EL_SPRING, ACTION_FALLING, -1
5494 Yspring_sB, FALSE, TRUE,
5495 EL_SPRING, ACTION_FALLING, -1
5498 Yspring_e, FALSE, FALSE,
5499 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5502 Yspring_eB, FALSE, TRUE,
5503 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5506 Yspring_w, FALSE, FALSE,
5507 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5510 Yspring_wB, FALSE, TRUE,
5511 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5514 Yspring_kill_e, FALSE, FALSE,
5515 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5518 Yspring_kill_eB, FALSE, TRUE,
5519 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5522 Yspring_kill_w, FALSE, FALSE,
5523 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5526 Yspring_kill_wB, FALSE, TRUE,
5527 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5530 Xeater_n, TRUE, FALSE,
5531 EL_YAMYAM_UP, -1, -1
5534 Xeater_e, TRUE, FALSE,
5535 EL_YAMYAM_RIGHT, -1, -1
5538 Xeater_w, TRUE, FALSE,
5539 EL_YAMYAM_LEFT, -1, -1
5542 Xeater_s, TRUE, FALSE,
5543 EL_YAMYAM_DOWN, -1, -1
5546 Yeater_n, FALSE, FALSE,
5547 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5550 Yeater_nB, FALSE, TRUE,
5551 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5554 Yeater_e, FALSE, FALSE,
5555 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5558 Yeater_eB, FALSE, TRUE,
5559 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5562 Yeater_s, FALSE, FALSE,
5563 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5566 Yeater_sB, FALSE, TRUE,
5567 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5570 Yeater_w, FALSE, FALSE,
5571 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5574 Yeater_wB, FALSE, TRUE,
5575 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5578 Yeater_stone, FALSE, FALSE,
5579 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5582 Yeater_spring, FALSE, FALSE,
5583 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5586 Xalien, TRUE, FALSE,
5590 Xalien_pause, FALSE, FALSE,
5594 Yalien_n, FALSE, FALSE,
5595 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5598 Yalien_nB, FALSE, TRUE,
5599 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5602 Yalien_e, FALSE, FALSE,
5603 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5606 Yalien_eB, FALSE, TRUE,
5607 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5610 Yalien_s, FALSE, FALSE,
5611 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5614 Yalien_sB, FALSE, TRUE,
5615 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5618 Yalien_w, FALSE, FALSE,
5619 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5622 Yalien_wB, FALSE, TRUE,
5623 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5626 Yalien_stone, FALSE, FALSE,
5627 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5630 Yalien_spring, FALSE, FALSE,
5631 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5634 Xemerald, TRUE, FALSE,
5638 Xemerald_pause, FALSE, FALSE,
5642 Xemerald_fall, FALSE, FALSE,
5646 Xemerald_shine, FALSE, FALSE,
5647 EL_EMERALD, ACTION_TWINKLING, -1
5650 Yemerald_s, FALSE, FALSE,
5651 EL_EMERALD, ACTION_FALLING, -1
5654 Yemerald_sB, FALSE, TRUE,
5655 EL_EMERALD, ACTION_FALLING, -1
5658 Yemerald_e, FALSE, FALSE,
5659 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5662 Yemerald_eB, FALSE, TRUE,
5663 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5666 Yemerald_w, FALSE, FALSE,
5667 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5670 Yemerald_wB, FALSE, TRUE,
5671 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5674 Yemerald_eat, FALSE, FALSE,
5675 EL_EMERALD, ACTION_COLLECTING, -1
5678 Yemerald_stone, FALSE, FALSE,
5679 EL_NUT, ACTION_BREAKING, -1
5682 Xdiamond, TRUE, FALSE,
5686 Xdiamond_pause, FALSE, FALSE,
5690 Xdiamond_fall, FALSE, FALSE,
5694 Xdiamond_shine, FALSE, FALSE,
5695 EL_DIAMOND, ACTION_TWINKLING, -1
5698 Ydiamond_s, FALSE, FALSE,
5699 EL_DIAMOND, ACTION_FALLING, -1
5702 Ydiamond_sB, FALSE, TRUE,
5703 EL_DIAMOND, ACTION_FALLING, -1
5706 Ydiamond_e, FALSE, FALSE,
5707 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5710 Ydiamond_eB, FALSE, TRUE,
5711 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5714 Ydiamond_w, FALSE, FALSE,
5715 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5718 Ydiamond_wB, FALSE, TRUE,
5719 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5722 Ydiamond_eat, FALSE, FALSE,
5723 EL_DIAMOND, ACTION_COLLECTING, -1
5726 Ydiamond_stone, FALSE, FALSE,
5727 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5730 Xdrip_fall, TRUE, FALSE,
5731 EL_AMOEBA_DROP, -1, -1
5734 Xdrip_stretch, FALSE, FALSE,
5735 EL_AMOEBA_DROP, ACTION_FALLING, -1
5738 Xdrip_stretchB, FALSE, TRUE,
5739 EL_AMOEBA_DROP, ACTION_FALLING, -1
5742 Xdrip_eat, FALSE, FALSE,
5743 EL_AMOEBA_DROP, ACTION_GROWING, -1
5746 Ydrip_s1, FALSE, FALSE,
5747 EL_AMOEBA_DROP, ACTION_FALLING, -1
5750 Ydrip_s1B, FALSE, TRUE,
5751 EL_AMOEBA_DROP, ACTION_FALLING, -1
5754 Ydrip_s2, FALSE, FALSE,
5755 EL_AMOEBA_DROP, ACTION_FALLING, -1
5758 Ydrip_s2B, FALSE, TRUE,
5759 EL_AMOEBA_DROP, ACTION_FALLING, -1
5766 Xbomb_pause, FALSE, FALSE,
5770 Xbomb_fall, FALSE, FALSE,
5774 Ybomb_s, FALSE, FALSE,
5775 EL_BOMB, ACTION_FALLING, -1
5778 Ybomb_sB, FALSE, TRUE,
5779 EL_BOMB, ACTION_FALLING, -1
5782 Ybomb_e, FALSE, FALSE,
5783 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5786 Ybomb_eB, FALSE, TRUE,
5787 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5790 Ybomb_w, FALSE, FALSE,
5791 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5794 Ybomb_wB, FALSE, TRUE,
5795 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5798 Ybomb_eat, FALSE, FALSE,
5799 EL_BOMB, ACTION_ACTIVATING, -1
5802 Xballoon, TRUE, FALSE,
5806 Yballoon_n, FALSE, FALSE,
5807 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5810 Yballoon_nB, FALSE, TRUE,
5811 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5814 Yballoon_e, FALSE, FALSE,
5815 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5818 Yballoon_eB, FALSE, TRUE,
5819 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5822 Yballoon_s, FALSE, FALSE,
5823 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5826 Yballoon_sB, FALSE, TRUE,
5827 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5830 Yballoon_w, FALSE, FALSE,
5831 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5834 Yballoon_wB, FALSE, TRUE,
5835 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5838 Xgrass, TRUE, FALSE,
5839 EL_EMC_GRASS, -1, -1
5842 Ygrass_nB, FALSE, FALSE,
5843 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5846 Ygrass_eB, FALSE, FALSE,
5847 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5850 Ygrass_sB, FALSE, FALSE,
5851 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5854 Ygrass_wB, FALSE, FALSE,
5855 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5862 Ydirt_nB, FALSE, FALSE,
5863 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5866 Ydirt_eB, FALSE, FALSE,
5867 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5870 Ydirt_sB, FALSE, FALSE,
5871 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5874 Ydirt_wB, FALSE, FALSE,
5875 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5878 Xacid_ne, TRUE, FALSE,
5879 EL_ACID_POOL_TOPRIGHT, -1, -1
5882 Xacid_se, TRUE, FALSE,
5883 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5886 Xacid_s, TRUE, FALSE,
5887 EL_ACID_POOL_BOTTOM, -1, -1
5890 Xacid_sw, TRUE, FALSE,
5891 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5894 Xacid_nw, TRUE, FALSE,
5895 EL_ACID_POOL_TOPLEFT, -1, -1
5898 Xacid_1, TRUE, FALSE,
5902 Xacid_2, FALSE, FALSE,
5906 Xacid_3, FALSE, FALSE,
5910 Xacid_4, FALSE, FALSE,
5914 Xacid_5, FALSE, FALSE,
5918 Xacid_6, FALSE, FALSE,
5922 Xacid_7, FALSE, FALSE,
5926 Xacid_8, FALSE, FALSE,
5930 Xball_1, TRUE, FALSE,
5931 EL_EMC_MAGIC_BALL, -1, -1
5934 Xball_1B, FALSE, FALSE,
5935 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5938 Xball_2, FALSE, FALSE,
5939 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5942 Xball_2B, FALSE, FALSE,
5943 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5946 Yball_eat, FALSE, FALSE,
5947 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5950 Ykey_1_eat, FALSE, FALSE,
5951 EL_EM_KEY_1, ACTION_COLLECTING, -1
5954 Ykey_2_eat, FALSE, FALSE,
5955 EL_EM_KEY_2, ACTION_COLLECTING, -1
5958 Ykey_3_eat, FALSE, FALSE,
5959 EL_EM_KEY_3, ACTION_COLLECTING, -1
5962 Ykey_4_eat, FALSE, FALSE,
5963 EL_EM_KEY_4, ACTION_COLLECTING, -1
5966 Ykey_5_eat, FALSE, FALSE,
5967 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5970 Ykey_6_eat, FALSE, FALSE,
5971 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5974 Ykey_7_eat, FALSE, FALSE,
5975 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5978 Ykey_8_eat, FALSE, FALSE,
5979 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5982 Ylenses_eat, FALSE, FALSE,
5983 EL_EMC_LENSES, ACTION_COLLECTING, -1
5986 Ymagnify_eat, FALSE, FALSE,
5987 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5990 Ygrass_eat, FALSE, FALSE,
5991 EL_EMC_GRASS, ACTION_SNAPPING, -1
5994 Ydirt_eat, FALSE, FALSE,
5995 EL_SAND, ACTION_SNAPPING, -1
5998 Xgrow_ns, TRUE, FALSE,
5999 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6002 Ygrow_ns_eat, FALSE, FALSE,
6003 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6006 Xgrow_ew, TRUE, FALSE,
6007 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6010 Ygrow_ew_eat, FALSE, FALSE,
6011 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6014 Xwonderwall, TRUE, FALSE,
6015 EL_MAGIC_WALL, -1, -1
6018 XwonderwallB, FALSE, FALSE,
6019 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6022 Xamoeba_1, TRUE, FALSE,
6023 EL_AMOEBA_DRY, ACTION_OTHER, -1
6026 Xamoeba_2, FALSE, FALSE,
6027 EL_AMOEBA_DRY, ACTION_OTHER, -1
6030 Xamoeba_3, FALSE, FALSE,
6031 EL_AMOEBA_DRY, ACTION_OTHER, -1
6034 Xamoeba_4, FALSE, FALSE,
6035 EL_AMOEBA_DRY, ACTION_OTHER, -1
6038 Xamoeba_5, TRUE, FALSE,
6039 EL_AMOEBA_WET, ACTION_OTHER, -1
6042 Xamoeba_6, FALSE, FALSE,
6043 EL_AMOEBA_WET, ACTION_OTHER, -1
6046 Xamoeba_7, FALSE, FALSE,
6047 EL_AMOEBA_WET, ACTION_OTHER, -1
6050 Xamoeba_8, FALSE, FALSE,
6051 EL_AMOEBA_WET, ACTION_OTHER, -1
6054 Xdoor_1, TRUE, FALSE,
6055 EL_EM_GATE_1, -1, -1
6058 Xdoor_2, TRUE, FALSE,
6059 EL_EM_GATE_2, -1, -1
6062 Xdoor_3, TRUE, FALSE,
6063 EL_EM_GATE_3, -1, -1
6066 Xdoor_4, TRUE, FALSE,
6067 EL_EM_GATE_4, -1, -1
6070 Xdoor_5, TRUE, FALSE,
6071 EL_EMC_GATE_5, -1, -1
6074 Xdoor_6, TRUE, FALSE,
6075 EL_EMC_GATE_6, -1, -1
6078 Xdoor_7, TRUE, FALSE,
6079 EL_EMC_GATE_7, -1, -1
6082 Xdoor_8, TRUE, FALSE,
6083 EL_EMC_GATE_8, -1, -1
6086 Xkey_1, TRUE, FALSE,
6090 Xkey_2, TRUE, FALSE,
6094 Xkey_3, TRUE, FALSE,
6098 Xkey_4, TRUE, FALSE,
6102 Xkey_5, TRUE, FALSE,
6103 EL_EMC_KEY_5, -1, -1
6106 Xkey_6, TRUE, FALSE,
6107 EL_EMC_KEY_6, -1, -1
6110 Xkey_7, TRUE, FALSE,
6111 EL_EMC_KEY_7, -1, -1
6114 Xkey_8, TRUE, FALSE,
6115 EL_EMC_KEY_8, -1, -1
6118 Xwind_n, TRUE, FALSE,
6119 EL_BALLOON_SWITCH_UP, -1, -1
6122 Xwind_e, TRUE, FALSE,
6123 EL_BALLOON_SWITCH_RIGHT, -1, -1
6126 Xwind_s, TRUE, FALSE,
6127 EL_BALLOON_SWITCH_DOWN, -1, -1
6130 Xwind_w, TRUE, FALSE,
6131 EL_BALLOON_SWITCH_LEFT, -1, -1
6134 Xwind_nesw, TRUE, FALSE,
6135 EL_BALLOON_SWITCH_ANY, -1, -1
6138 Xwind_stop, TRUE, FALSE,
6139 EL_BALLOON_SWITCH_NONE, -1, -1
6143 EL_EM_EXIT_CLOSED, -1, -1
6146 Xexit_1, TRUE, FALSE,
6147 EL_EM_EXIT_OPEN, -1, -1
6150 Xexit_2, FALSE, FALSE,
6151 EL_EM_EXIT_OPEN, -1, -1
6154 Xexit_3, FALSE, FALSE,
6155 EL_EM_EXIT_OPEN, -1, -1
6158 Xdynamite, TRUE, FALSE,
6159 EL_EM_DYNAMITE, -1, -1
6162 Ydynamite_eat, FALSE, FALSE,
6163 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6166 Xdynamite_1, TRUE, FALSE,
6167 EL_EM_DYNAMITE_ACTIVE, -1, -1
6170 Xdynamite_2, FALSE, FALSE,
6171 EL_EM_DYNAMITE_ACTIVE, -1, -1
6174 Xdynamite_3, FALSE, FALSE,
6175 EL_EM_DYNAMITE_ACTIVE, -1, -1
6178 Xdynamite_4, FALSE, FALSE,
6179 EL_EM_DYNAMITE_ACTIVE, -1, -1
6182 Xbumper, TRUE, FALSE,
6183 EL_EMC_SPRING_BUMPER, -1, -1
6186 XbumperB, FALSE, FALSE,
6187 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6190 Xwheel, TRUE, FALSE,
6191 EL_ROBOT_WHEEL, -1, -1
6194 XwheelB, FALSE, FALSE,
6195 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6198 Xswitch, TRUE, FALSE,
6199 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6202 XswitchB, FALSE, FALSE,
6203 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6207 EL_QUICKSAND_EMPTY, -1, -1
6210 Xsand_stone, TRUE, FALSE,
6211 EL_QUICKSAND_FULL, -1, -1
6214 Xsand_stonein_1, FALSE, TRUE,
6215 EL_ROCK, ACTION_FILLING, -1
6218 Xsand_stonein_2, FALSE, TRUE,
6219 EL_ROCK, ACTION_FILLING, -1
6222 Xsand_stonein_3, FALSE, TRUE,
6223 EL_ROCK, ACTION_FILLING, -1
6226 Xsand_stonein_4, FALSE, TRUE,
6227 EL_ROCK, ACTION_FILLING, -1
6230 Xsand_stonesand_1, FALSE, FALSE,
6231 EL_QUICKSAND_EMPTYING, -1, -1
6234 Xsand_stonesand_2, FALSE, FALSE,
6235 EL_QUICKSAND_EMPTYING, -1, -1
6238 Xsand_stonesand_3, FALSE, FALSE,
6239 EL_QUICKSAND_EMPTYING, -1, -1
6242 Xsand_stonesand_4, FALSE, FALSE,
6243 EL_QUICKSAND_EMPTYING, -1, -1
6246 Xsand_stonesand_quickout_1, FALSE, FALSE,
6247 EL_QUICKSAND_EMPTYING, -1, -1
6250 Xsand_stonesand_quickout_2, FALSE, FALSE,
6251 EL_QUICKSAND_EMPTYING, -1, -1
6254 Xsand_stoneout_1, FALSE, FALSE,
6255 EL_ROCK, ACTION_EMPTYING, -1
6258 Xsand_stoneout_2, FALSE, FALSE,
6259 EL_ROCK, ACTION_EMPTYING, -1
6262 Xsand_sandstone_1, FALSE, FALSE,
6263 EL_QUICKSAND_FILLING, -1, -1
6266 Xsand_sandstone_2, FALSE, FALSE,
6267 EL_QUICKSAND_FILLING, -1, -1
6270 Xsand_sandstone_3, FALSE, FALSE,
6271 EL_QUICKSAND_FILLING, -1, -1
6274 Xsand_sandstone_4, FALSE, FALSE,
6275 EL_QUICKSAND_FILLING, -1, -1
6278 Xplant, TRUE, FALSE,
6279 EL_EMC_PLANT, -1, -1
6282 Yplant, FALSE, FALSE,
6283 EL_EMC_PLANT, -1, -1
6286 Xlenses, TRUE, FALSE,
6287 EL_EMC_LENSES, -1, -1
6290 Xmagnify, TRUE, FALSE,
6291 EL_EMC_MAGNIFIER, -1, -1
6294 Xdripper, TRUE, FALSE,
6295 EL_EMC_DRIPPER, -1, -1
6298 XdripperB, FALSE, FALSE,
6299 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6302 Xfake_blank, TRUE, FALSE,
6303 EL_INVISIBLE_WALL, -1, -1
6306 Xfake_blankB, FALSE, FALSE,
6307 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6310 Xfake_grass, TRUE, FALSE,
6311 EL_EMC_FAKE_GRASS, -1, -1
6314 Xfake_grassB, FALSE, FALSE,
6315 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6318 Xfake_door_1, TRUE, FALSE,
6319 EL_EM_GATE_1_GRAY, -1, -1
6322 Xfake_door_2, TRUE, FALSE,
6323 EL_EM_GATE_2_GRAY, -1, -1
6326 Xfake_door_3, TRUE, FALSE,
6327 EL_EM_GATE_3_GRAY, -1, -1
6330 Xfake_door_4, TRUE, FALSE,
6331 EL_EM_GATE_4_GRAY, -1, -1
6334 Xfake_door_5, TRUE, FALSE,
6335 EL_EMC_GATE_5_GRAY, -1, -1
6338 Xfake_door_6, TRUE, FALSE,
6339 EL_EMC_GATE_6_GRAY, -1, -1
6342 Xfake_door_7, TRUE, FALSE,
6343 EL_EMC_GATE_7_GRAY, -1, -1
6346 Xfake_door_8, TRUE, FALSE,
6347 EL_EMC_GATE_8_GRAY, -1, -1
6350 Xfake_acid_1, TRUE, FALSE,
6351 EL_EMC_FAKE_ACID, -1, -1
6354 Xfake_acid_2, FALSE, FALSE,
6355 EL_EMC_FAKE_ACID, -1, -1
6358 Xfake_acid_3, FALSE, FALSE,
6359 EL_EMC_FAKE_ACID, -1, -1
6362 Xfake_acid_4, FALSE, FALSE,
6363 EL_EMC_FAKE_ACID, -1, -1
6366 Xfake_acid_5, FALSE, FALSE,
6367 EL_EMC_FAKE_ACID, -1, -1
6370 Xfake_acid_6, FALSE, FALSE,
6371 EL_EMC_FAKE_ACID, -1, -1
6374 Xfake_acid_7, FALSE, FALSE,
6375 EL_EMC_FAKE_ACID, -1, -1
6378 Xfake_acid_8, FALSE, FALSE,
6379 EL_EMC_FAKE_ACID, -1, -1
6382 Xsteel_1, TRUE, FALSE,
6383 EL_STEELWALL, -1, -1
6386 Xsteel_2, TRUE, FALSE,
6387 EL_EMC_STEELWALL_2, -1, -1
6390 Xsteel_3, TRUE, FALSE,
6391 EL_EMC_STEELWALL_3, -1, -1
6394 Xsteel_4, TRUE, FALSE,
6395 EL_EMC_STEELWALL_4, -1, -1
6398 Xwall_1, TRUE, FALSE,
6402 Xwall_2, TRUE, FALSE,
6403 EL_EMC_WALL_14, -1, -1
6406 Xwall_3, TRUE, FALSE,
6407 EL_EMC_WALL_15, -1, -1
6410 Xwall_4, TRUE, FALSE,
6411 EL_EMC_WALL_16, -1, -1
6414 Xround_wall_1, TRUE, FALSE,
6415 EL_WALL_SLIPPERY, -1, -1
6418 Xround_wall_2, TRUE, FALSE,
6419 EL_EMC_WALL_SLIPPERY_2, -1, -1
6422 Xround_wall_3, TRUE, FALSE,
6423 EL_EMC_WALL_SLIPPERY_3, -1, -1
6426 Xround_wall_4, TRUE, FALSE,
6427 EL_EMC_WALL_SLIPPERY_4, -1, -1
6430 Xdecor_1, TRUE, FALSE,
6431 EL_EMC_WALL_8, -1, -1
6434 Xdecor_2, TRUE, FALSE,
6435 EL_EMC_WALL_6, -1, -1
6438 Xdecor_3, TRUE, FALSE,
6439 EL_EMC_WALL_4, -1, -1
6442 Xdecor_4, TRUE, FALSE,
6443 EL_EMC_WALL_7, -1, -1
6446 Xdecor_5, TRUE, FALSE,
6447 EL_EMC_WALL_5, -1, -1
6450 Xdecor_6, TRUE, FALSE,
6451 EL_EMC_WALL_9, -1, -1
6454 Xdecor_7, TRUE, FALSE,
6455 EL_EMC_WALL_10, -1, -1
6458 Xdecor_8, TRUE, FALSE,
6459 EL_EMC_WALL_1, -1, -1
6462 Xdecor_9, TRUE, FALSE,
6463 EL_EMC_WALL_2, -1, -1
6466 Xdecor_10, TRUE, FALSE,
6467 EL_EMC_WALL_3, -1, -1
6470 Xdecor_11, TRUE, FALSE,
6471 EL_EMC_WALL_11, -1, -1
6474 Xdecor_12, TRUE, FALSE,
6475 EL_EMC_WALL_12, -1, -1
6478 Xalpha_0, TRUE, FALSE,
6479 EL_CHAR('0'), -1, -1
6482 Xalpha_1, TRUE, FALSE,
6483 EL_CHAR('1'), -1, -1
6486 Xalpha_2, TRUE, FALSE,
6487 EL_CHAR('2'), -1, -1
6490 Xalpha_3, TRUE, FALSE,
6491 EL_CHAR('3'), -1, -1
6494 Xalpha_4, TRUE, FALSE,
6495 EL_CHAR('4'), -1, -1
6498 Xalpha_5, TRUE, FALSE,
6499 EL_CHAR('5'), -1, -1
6502 Xalpha_6, TRUE, FALSE,
6503 EL_CHAR('6'), -1, -1
6506 Xalpha_7, TRUE, FALSE,
6507 EL_CHAR('7'), -1, -1
6510 Xalpha_8, TRUE, FALSE,
6511 EL_CHAR('8'), -1, -1
6514 Xalpha_9, TRUE, FALSE,
6515 EL_CHAR('9'), -1, -1
6518 Xalpha_excla, TRUE, FALSE,
6519 EL_CHAR('!'), -1, -1
6522 Xalpha_quote, TRUE, FALSE,
6523 EL_CHAR('"'), -1, -1
6526 Xalpha_comma, TRUE, FALSE,
6527 EL_CHAR(','), -1, -1
6530 Xalpha_minus, TRUE, FALSE,
6531 EL_CHAR('-'), -1, -1
6534 Xalpha_perio, TRUE, FALSE,
6535 EL_CHAR('.'), -1, -1
6538 Xalpha_colon, TRUE, FALSE,
6539 EL_CHAR(':'), -1, -1
6542 Xalpha_quest, TRUE, FALSE,
6543 EL_CHAR('?'), -1, -1
6546 Xalpha_a, TRUE, FALSE,
6547 EL_CHAR('A'), -1, -1
6550 Xalpha_b, TRUE, FALSE,
6551 EL_CHAR('B'), -1, -1
6554 Xalpha_c, TRUE, FALSE,
6555 EL_CHAR('C'), -1, -1
6558 Xalpha_d, TRUE, FALSE,
6559 EL_CHAR('D'), -1, -1
6562 Xalpha_e, TRUE, FALSE,
6563 EL_CHAR('E'), -1, -1
6566 Xalpha_f, TRUE, FALSE,
6567 EL_CHAR('F'), -1, -1
6570 Xalpha_g, TRUE, FALSE,
6571 EL_CHAR('G'), -1, -1
6574 Xalpha_h, TRUE, FALSE,
6575 EL_CHAR('H'), -1, -1
6578 Xalpha_i, TRUE, FALSE,
6579 EL_CHAR('I'), -1, -1
6582 Xalpha_j, TRUE, FALSE,
6583 EL_CHAR('J'), -1, -1
6586 Xalpha_k, TRUE, FALSE,
6587 EL_CHAR('K'), -1, -1
6590 Xalpha_l, TRUE, FALSE,
6591 EL_CHAR('L'), -1, -1
6594 Xalpha_m, TRUE, FALSE,
6595 EL_CHAR('M'), -1, -1
6598 Xalpha_n, TRUE, FALSE,
6599 EL_CHAR('N'), -1, -1
6602 Xalpha_o, TRUE, FALSE,
6603 EL_CHAR('O'), -1, -1
6606 Xalpha_p, TRUE, FALSE,
6607 EL_CHAR('P'), -1, -1
6610 Xalpha_q, TRUE, FALSE,
6611 EL_CHAR('Q'), -1, -1
6614 Xalpha_r, TRUE, FALSE,
6615 EL_CHAR('R'), -1, -1
6618 Xalpha_s, TRUE, FALSE,
6619 EL_CHAR('S'), -1, -1
6622 Xalpha_t, TRUE, FALSE,
6623 EL_CHAR('T'), -1, -1
6626 Xalpha_u, TRUE, FALSE,
6627 EL_CHAR('U'), -1, -1
6630 Xalpha_v, TRUE, FALSE,
6631 EL_CHAR('V'), -1, -1
6634 Xalpha_w, TRUE, FALSE,
6635 EL_CHAR('W'), -1, -1
6638 Xalpha_x, TRUE, FALSE,
6639 EL_CHAR('X'), -1, -1
6642 Xalpha_y, TRUE, FALSE,
6643 EL_CHAR('Y'), -1, -1
6646 Xalpha_z, TRUE, FALSE,
6647 EL_CHAR('Z'), -1, -1
6650 Xalpha_arrow_e, TRUE, FALSE,
6651 EL_CHAR('>'), -1, -1
6654 Xalpha_arrow_w, TRUE, FALSE,
6655 EL_CHAR('<'), -1, -1
6658 Xalpha_copyr, TRUE, FALSE,
6659 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6663 Xboom_bug, FALSE, FALSE,
6664 EL_BUG, ACTION_EXPLODING, -1
6667 Xboom_bomb, FALSE, FALSE,
6668 EL_BOMB, ACTION_EXPLODING, -1
6671 Xboom_android, FALSE, FALSE,
6672 EL_EMC_ANDROID, ACTION_OTHER, -1
6675 Xboom_1, FALSE, FALSE,
6676 EL_DEFAULT, ACTION_EXPLODING, -1
6679 Xboom_2, FALSE, FALSE,
6680 EL_DEFAULT, ACTION_EXPLODING, -1
6683 Znormal, FALSE, FALSE,
6687 Zdynamite, FALSE, FALSE,
6691 Zplayer, FALSE, FALSE,
6695 ZBORDER, FALSE, FALSE,
6705 static struct Mapping_EM_to_RND_player
6714 em_player_mapping_list[] =
6718 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6722 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6726 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6730 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6734 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6738 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6742 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6746 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6750 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6754 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6758 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6762 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6766 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6770 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6774 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6778 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6782 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6786 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6790 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6794 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6798 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6802 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6806 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6810 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6814 EL_PLAYER_1, ACTION_DEFAULT, -1,
6818 EL_PLAYER_2, ACTION_DEFAULT, -1,
6822 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6826 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6830 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6834 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6838 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6842 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6846 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6850 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6854 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6858 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6862 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6866 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6870 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6874 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6878 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6882 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6886 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6890 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6894 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6898 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6902 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6906 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6910 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6914 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6918 EL_PLAYER_3, ACTION_DEFAULT, -1,
6922 EL_PLAYER_4, ACTION_DEFAULT, -1,
6931 int map_element_RND_to_EM(int element_rnd)
6933 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6934 static boolean mapping_initialized = FALSE;
6936 if (!mapping_initialized)
6940 /* return "Xalpha_quest" for all undefined elements in mapping array */
6941 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6942 mapping_RND_to_EM[i] = Xalpha_quest;
6944 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6945 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6946 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6947 em_object_mapping_list[i].element_em;
6949 mapping_initialized = TRUE;
6952 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6953 return mapping_RND_to_EM[element_rnd];
6955 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6960 int map_element_EM_to_RND(int element_em)
6962 static unsigned short mapping_EM_to_RND[TILE_MAX];
6963 static boolean mapping_initialized = FALSE;
6965 if (!mapping_initialized)
6969 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6970 for (i = 0; i < TILE_MAX; i++)
6971 mapping_EM_to_RND[i] = EL_UNKNOWN;
6973 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6974 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6975 em_object_mapping_list[i].element_rnd;
6977 mapping_initialized = TRUE;
6980 if (element_em >= 0 && element_em < TILE_MAX)
6981 return mapping_EM_to_RND[element_em];
6983 Error(ERR_WARN, "invalid EM level element %d", element_em);
6988 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6990 struct LevelInfo_EM *level_em = level->native_em_level;
6991 struct LEVEL *lev = level_em->lev;
6994 for (i = 0; i < TILE_MAX; i++)
6995 lev->android_array[i] = Xblank;
6997 for (i = 0; i < level->num_android_clone_elements; i++)
6999 int element_rnd = level->android_clone_element[i];
7000 int element_em = map_element_RND_to_EM(element_rnd);
7002 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7003 if (em_object_mapping_list[j].element_rnd == element_rnd)
7004 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7008 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7010 struct LevelInfo_EM *level_em = level->native_em_level;
7011 struct LEVEL *lev = level_em->lev;
7014 level->num_android_clone_elements = 0;
7016 for (i = 0; i < TILE_MAX; i++)
7018 int element_em = lev->android_array[i];
7020 boolean element_found = FALSE;
7022 if (element_em == Xblank)
7025 element_rnd = map_element_EM_to_RND(element_em);
7027 for (j = 0; j < level->num_android_clone_elements; j++)
7028 if (level->android_clone_element[j] == element_rnd)
7029 element_found = TRUE;
7033 level->android_clone_element[level->num_android_clone_elements++] =
7036 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7041 if (level->num_android_clone_elements == 0)
7043 level->num_android_clone_elements = 1;
7044 level->android_clone_element[0] = EL_EMPTY;
7048 int map_direction_RND_to_EM(int direction)
7050 return (direction == MV_UP ? 0 :
7051 direction == MV_RIGHT ? 1 :
7052 direction == MV_DOWN ? 2 :
7053 direction == MV_LEFT ? 3 :
7057 int map_direction_EM_to_RND(int direction)
7059 return (direction == 0 ? MV_UP :
7060 direction == 1 ? MV_RIGHT :
7061 direction == 2 ? MV_DOWN :
7062 direction == 3 ? MV_LEFT :
7066 int map_element_RND_to_SP(int element_rnd)
7068 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7070 if (element_rnd >= EL_SP_START &&
7071 element_rnd <= EL_SP_END)
7072 element_sp = element_rnd - EL_SP_START;
7073 else if (element_rnd == EL_EMPTY_SPACE)
7075 else if (element_rnd == EL_INVISIBLE_WALL)
7081 int map_element_SP_to_RND(int element_sp)
7083 int element_rnd = EL_UNKNOWN;
7085 if (element_sp >= 0x00 &&
7087 element_rnd = EL_SP_START + element_sp;
7088 else if (element_sp == 0x28)
7089 element_rnd = EL_INVISIBLE_WALL;
7094 int map_action_SP_to_RND(int action_sp)
7098 case actActive: return ACTION_ACTIVE;
7099 case actImpact: return ACTION_IMPACT;
7100 case actExploding: return ACTION_EXPLODING;
7101 case actDigging: return ACTION_DIGGING;
7102 case actSnapping: return ACTION_SNAPPING;
7103 case actCollecting: return ACTION_COLLECTING;
7104 case actPassing: return ACTION_PASSING;
7105 case actPushing: return ACTION_PUSHING;
7106 case actDropping: return ACTION_DROPPING;
7108 default: return ACTION_DEFAULT;
7112 int get_next_element(int element)
7116 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7117 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7118 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7119 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7120 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7121 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7122 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7123 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7124 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7125 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7126 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7128 default: return element;
7132 int el_act_dir2img(int element, int action, int direction)
7134 element = GFX_ELEMENT(element);
7135 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7137 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7138 return element_info[element].direction_graphic[action][direction];
7141 static int el_act_dir2crm(int element, int action, int direction)
7143 element = GFX_ELEMENT(element);
7144 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7146 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7147 return element_info[element].direction_crumbled[action][direction];
7150 int el_act2img(int element, int action)
7152 element = GFX_ELEMENT(element);
7154 return element_info[element].graphic[action];
7157 int el_act2crm(int element, int action)
7159 element = GFX_ELEMENT(element);
7161 return element_info[element].crumbled[action];
7164 int el_dir2img(int element, int direction)
7166 element = GFX_ELEMENT(element);
7168 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7171 int el2baseimg(int element)
7173 return element_info[element].graphic[ACTION_DEFAULT];
7176 int el2img(int element)
7178 element = GFX_ELEMENT(element);
7180 return element_info[element].graphic[ACTION_DEFAULT];
7183 int el2edimg(int element)
7185 element = GFX_ELEMENT(element);
7187 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7190 int el2preimg(int element)
7192 element = GFX_ELEMENT(element);
7194 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7197 int el2panelimg(int element)
7199 element = GFX_ELEMENT(element);
7201 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7204 int font2baseimg(int font_nr)
7206 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7209 int getBeltNrFromBeltElement(int element)
7211 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7212 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7213 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7216 int getBeltNrFromBeltActiveElement(int element)
7218 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7219 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7220 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7223 int getBeltNrFromBeltSwitchElement(int element)
7225 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7226 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7227 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7230 int getBeltDirNrFromBeltElement(int element)
7232 static int belt_base_element[4] =
7234 EL_CONVEYOR_BELT_1_LEFT,
7235 EL_CONVEYOR_BELT_2_LEFT,
7236 EL_CONVEYOR_BELT_3_LEFT,
7237 EL_CONVEYOR_BELT_4_LEFT
7240 int belt_nr = getBeltNrFromBeltElement(element);
7241 int belt_dir_nr = element - belt_base_element[belt_nr];
7243 return (belt_dir_nr % 3);
7246 int getBeltDirNrFromBeltSwitchElement(int element)
7248 static int belt_base_element[4] =
7250 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7251 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7252 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7253 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7256 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7257 int belt_dir_nr = element - belt_base_element[belt_nr];
7259 return (belt_dir_nr % 3);
7262 int getBeltDirFromBeltElement(int element)
7264 static int belt_move_dir[3] =
7271 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7273 return belt_move_dir[belt_dir_nr];
7276 int getBeltDirFromBeltSwitchElement(int element)
7278 static int belt_move_dir[3] =
7285 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7287 return belt_move_dir[belt_dir_nr];
7290 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7292 static int belt_base_element[4] =
7294 EL_CONVEYOR_BELT_1_LEFT,
7295 EL_CONVEYOR_BELT_2_LEFT,
7296 EL_CONVEYOR_BELT_3_LEFT,
7297 EL_CONVEYOR_BELT_4_LEFT
7300 return belt_base_element[belt_nr] + belt_dir_nr;
7303 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7305 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7307 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7310 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7312 static int belt_base_element[4] =
7314 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7315 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7316 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7317 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7320 return belt_base_element[belt_nr] + belt_dir_nr;
7323 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7325 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7327 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7330 boolean getTeamMode_EM()
7332 return game.team_mode;
7335 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7337 int game_frame_delay_value;
7339 game_frame_delay_value =
7340 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7341 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7344 if (tape.playing && tape.warp_forward && !tape.pausing)
7345 game_frame_delay_value = 0;
7347 return game_frame_delay_value;
7350 unsigned int InitRND(int seed)
7352 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7353 return InitEngineRandom_EM(seed);
7354 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7355 return InitEngineRandom_SP(seed);
7357 return InitEngineRandom_RND(seed);
7360 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7361 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7363 inline static int get_effective_element_EM(int tile, int frame_em)
7365 int element = object_mapping[tile].element_rnd;
7366 int action = object_mapping[tile].action;
7367 boolean is_backside = object_mapping[tile].is_backside;
7368 boolean action_removing = (action == ACTION_DIGGING ||
7369 action == ACTION_SNAPPING ||
7370 action == ACTION_COLLECTING);
7376 case Yacid_splash_eB:
7377 case Yacid_splash_wB:
7378 return (frame_em > 5 ? EL_EMPTY : element);
7384 else /* frame_em == 7 */
7388 case Yacid_splash_eB:
7389 case Yacid_splash_wB:
7392 case Yemerald_stone:
7395 case Ydiamond_stone:
7399 case Xdrip_stretchB:
7418 case Xsand_stonein_1:
7419 case Xsand_stonein_2:
7420 case Xsand_stonein_3:
7421 case Xsand_stonein_4:
7425 return (is_backside || action_removing ? EL_EMPTY : element);
7430 inline static boolean check_linear_animation_EM(int tile)
7434 case Xsand_stonesand_1:
7435 case Xsand_stonesand_quickout_1:
7436 case Xsand_sandstone_1:
7437 case Xsand_stonein_1:
7438 case Xsand_stoneout_1:
7457 case Yacid_splash_eB:
7458 case Yacid_splash_wB:
7459 case Yemerald_stone:
7466 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7467 boolean has_crumbled_graphics,
7468 int crumbled, int sync_frame)
7470 /* if element can be crumbled, but certain action graphics are just empty
7471 space (like instantly snapping sand to empty space in 1 frame), do not
7472 treat these empty space graphics as crumbled graphics in EMC engine */
7473 if (crumbled == IMG_EMPTY_SPACE)
7474 has_crumbled_graphics = FALSE;
7476 if (has_crumbled_graphics)
7478 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7479 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7480 g_crumbled->anim_delay,
7481 g_crumbled->anim_mode,
7482 g_crumbled->anim_start_frame,
7485 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7486 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7488 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7489 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7491 g_em->has_crumbled_graphics = TRUE;
7495 g_em->crumbled_bitmap = NULL;
7496 g_em->crumbled_src_x = 0;
7497 g_em->crumbled_src_y = 0;
7498 g_em->crumbled_border_size = 0;
7499 g_em->crumbled_tile_size = 0;
7501 g_em->has_crumbled_graphics = FALSE;
7505 void ResetGfxAnimation_EM(int x, int y, int tile)
7510 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7511 int tile, int frame_em, int x, int y)
7513 int action = object_mapping[tile].action;
7514 int direction = object_mapping[tile].direction;
7515 int effective_element = get_effective_element_EM(tile, frame_em);
7516 int graphic = (direction == MV_NONE ?
7517 el_act2img(effective_element, action) :
7518 el_act_dir2img(effective_element, action, direction));
7519 struct GraphicInfo *g = &graphic_info[graphic];
7521 boolean action_removing = (action == ACTION_DIGGING ||
7522 action == ACTION_SNAPPING ||
7523 action == ACTION_COLLECTING);
7524 boolean action_moving = (action == ACTION_FALLING ||
7525 action == ACTION_MOVING ||
7526 action == ACTION_PUSHING ||
7527 action == ACTION_EATING ||
7528 action == ACTION_FILLING ||
7529 action == ACTION_EMPTYING);
7530 boolean action_falling = (action == ACTION_FALLING ||
7531 action == ACTION_FILLING ||
7532 action == ACTION_EMPTYING);
7534 /* special case: graphic uses "2nd movement tile" and has defined
7535 7 frames for movement animation (or less) => use default graphic
7536 for last (8th) frame which ends the movement animation */
7537 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7539 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7540 graphic = (direction == MV_NONE ?
7541 el_act2img(effective_element, action) :
7542 el_act_dir2img(effective_element, action, direction));
7544 g = &graphic_info[graphic];
7547 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7551 else if (action_moving)
7553 boolean is_backside = object_mapping[tile].is_backside;
7557 int direction = object_mapping[tile].direction;
7558 int move_dir = (action_falling ? MV_DOWN : direction);
7563 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7564 if (g->double_movement && frame_em == 0)
7568 if (move_dir == MV_LEFT)
7569 GfxFrame[x - 1][y] = GfxFrame[x][y];
7570 else if (move_dir == MV_RIGHT)
7571 GfxFrame[x + 1][y] = GfxFrame[x][y];
7572 else if (move_dir == MV_UP)
7573 GfxFrame[x][y - 1] = GfxFrame[x][y];
7574 else if (move_dir == MV_DOWN)
7575 GfxFrame[x][y + 1] = GfxFrame[x][y];
7582 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7583 if (tile == Xsand_stonesand_quickout_1 ||
7584 tile == Xsand_stonesand_quickout_2)
7588 if (graphic_info[graphic].anim_global_sync)
7589 sync_frame = FrameCounter;
7590 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7591 sync_frame = GfxFrame[x][y];
7593 sync_frame = 0; /* playfield border (pseudo steel) */
7595 SetRandomAnimationValue(x, y);
7597 int frame = getAnimationFrame(g->anim_frames,
7600 g->anim_start_frame,
7603 g_em->unique_identifier =
7604 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7607 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7608 int tile, int frame_em, int x, int y)
7610 int action = object_mapping[tile].action;
7611 int direction = object_mapping[tile].direction;
7612 boolean is_backside = object_mapping[tile].is_backside;
7613 int effective_element = get_effective_element_EM(tile, frame_em);
7614 int effective_action = action;
7615 int graphic = (direction == MV_NONE ?
7616 el_act2img(effective_element, effective_action) :
7617 el_act_dir2img(effective_element, effective_action,
7619 int crumbled = (direction == MV_NONE ?
7620 el_act2crm(effective_element, effective_action) :
7621 el_act_dir2crm(effective_element, effective_action,
7623 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7624 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7625 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7626 struct GraphicInfo *g = &graphic_info[graphic];
7629 /* special case: graphic uses "2nd movement tile" and has defined
7630 7 frames for movement animation (or less) => use default graphic
7631 for last (8th) frame which ends the movement animation */
7632 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7634 effective_action = ACTION_DEFAULT;
7635 graphic = (direction == MV_NONE ?
7636 el_act2img(effective_element, effective_action) :
7637 el_act_dir2img(effective_element, effective_action,
7639 crumbled = (direction == MV_NONE ?
7640 el_act2crm(effective_element, effective_action) :
7641 el_act_dir2crm(effective_element, effective_action,
7644 g = &graphic_info[graphic];
7647 if (graphic_info[graphic].anim_global_sync)
7648 sync_frame = FrameCounter;
7649 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7650 sync_frame = GfxFrame[x][y];
7652 sync_frame = 0; /* playfield border (pseudo steel) */
7654 SetRandomAnimationValue(x, y);
7656 int frame = getAnimationFrame(g->anim_frames,
7659 g->anim_start_frame,
7662 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7663 g->double_movement && is_backside);
7665 /* (updating the "crumbled" graphic definitions is probably not really needed,
7666 as animations for crumbled graphics can't be longer than one EMC cycle) */
7667 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7671 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7672 int player_nr, int anim, int frame_em)
7674 int element = player_mapping[player_nr][anim].element_rnd;
7675 int action = player_mapping[player_nr][anim].action;
7676 int direction = player_mapping[player_nr][anim].direction;
7677 int graphic = (direction == MV_NONE ?
7678 el_act2img(element, action) :
7679 el_act_dir2img(element, action, direction));
7680 struct GraphicInfo *g = &graphic_info[graphic];
7683 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7685 stored_player[player_nr].StepFrame = frame_em;
7687 sync_frame = stored_player[player_nr].Frame;
7689 int frame = getAnimationFrame(g->anim_frames,
7692 g->anim_start_frame,
7695 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7696 &g_em->src_x, &g_em->src_y, FALSE);
7699 void InitGraphicInfo_EM(void)
7704 int num_em_gfx_errors = 0;
7706 if (graphic_info_em_object[0][0].bitmap == NULL)
7708 /* EM graphics not yet initialized in em_open_all() */
7713 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7716 /* always start with reliable default values */
7717 for (i = 0; i < TILE_MAX; i++)
7719 object_mapping[i].element_rnd = EL_UNKNOWN;
7720 object_mapping[i].is_backside = FALSE;
7721 object_mapping[i].action = ACTION_DEFAULT;
7722 object_mapping[i].direction = MV_NONE;
7725 /* always start with reliable default values */
7726 for (p = 0; p < MAX_PLAYERS; p++)
7728 for (i = 0; i < SPR_MAX; i++)
7730 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7731 player_mapping[p][i].action = ACTION_DEFAULT;
7732 player_mapping[p][i].direction = MV_NONE;
7736 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7738 int e = em_object_mapping_list[i].element_em;
7740 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7741 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7743 if (em_object_mapping_list[i].action != -1)
7744 object_mapping[e].action = em_object_mapping_list[i].action;
7746 if (em_object_mapping_list[i].direction != -1)
7747 object_mapping[e].direction =
7748 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7751 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7753 int a = em_player_mapping_list[i].action_em;
7754 int p = em_player_mapping_list[i].player_nr;
7756 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7758 if (em_player_mapping_list[i].action != -1)
7759 player_mapping[p][a].action = em_player_mapping_list[i].action;
7761 if (em_player_mapping_list[i].direction != -1)
7762 player_mapping[p][a].direction =
7763 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7766 for (i = 0; i < TILE_MAX; i++)
7768 int element = object_mapping[i].element_rnd;
7769 int action = object_mapping[i].action;
7770 int direction = object_mapping[i].direction;
7771 boolean is_backside = object_mapping[i].is_backside;
7772 boolean action_exploding = ((action == ACTION_EXPLODING ||
7773 action == ACTION_SMASHED_BY_ROCK ||
7774 action == ACTION_SMASHED_BY_SPRING) &&
7775 element != EL_DIAMOND);
7776 boolean action_active = (action == ACTION_ACTIVE);
7777 boolean action_other = (action == ACTION_OTHER);
7779 for (j = 0; j < 8; j++)
7781 int effective_element = get_effective_element_EM(i, j);
7782 int effective_action = (j < 7 ? action :
7783 i == Xdrip_stretch ? action :
7784 i == Xdrip_stretchB ? action :
7785 i == Ydrip_s1 ? action :
7786 i == Ydrip_s1B ? action :
7787 i == Xball_1B ? action :
7788 i == Xball_2 ? action :
7789 i == Xball_2B ? action :
7790 i == Yball_eat ? action :
7791 i == Ykey_1_eat ? action :
7792 i == Ykey_2_eat ? action :
7793 i == Ykey_3_eat ? action :
7794 i == Ykey_4_eat ? action :
7795 i == Ykey_5_eat ? action :
7796 i == Ykey_6_eat ? action :
7797 i == Ykey_7_eat ? action :
7798 i == Ykey_8_eat ? action :
7799 i == Ylenses_eat ? action :
7800 i == Ymagnify_eat ? action :
7801 i == Ygrass_eat ? action :
7802 i == Ydirt_eat ? action :
7803 i == Xsand_stonein_1 ? action :
7804 i == Xsand_stonein_2 ? action :
7805 i == Xsand_stonein_3 ? action :
7806 i == Xsand_stonein_4 ? action :
7807 i == Xsand_stoneout_1 ? action :
7808 i == Xsand_stoneout_2 ? action :
7809 i == Xboom_android ? ACTION_EXPLODING :
7810 action_exploding ? ACTION_EXPLODING :
7811 action_active ? action :
7812 action_other ? action :
7814 int graphic = (el_act_dir2img(effective_element, effective_action,
7816 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7818 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7819 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7820 boolean has_action_graphics = (graphic != base_graphic);
7821 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7822 struct GraphicInfo *g = &graphic_info[graphic];
7823 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7826 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7827 boolean special_animation = (action != ACTION_DEFAULT &&
7828 g->anim_frames == 3 &&
7829 g->anim_delay == 2 &&
7830 g->anim_mode & ANIM_LINEAR);
7831 int sync_frame = (i == Xdrip_stretch ? 7 :
7832 i == Xdrip_stretchB ? 7 :
7833 i == Ydrip_s2 ? j + 8 :
7834 i == Ydrip_s2B ? j + 8 :
7843 i == Xfake_acid_1 ? 0 :
7844 i == Xfake_acid_2 ? 10 :
7845 i == Xfake_acid_3 ? 20 :
7846 i == Xfake_acid_4 ? 30 :
7847 i == Xfake_acid_5 ? 40 :
7848 i == Xfake_acid_6 ? 50 :
7849 i == Xfake_acid_7 ? 60 :
7850 i == Xfake_acid_8 ? 70 :
7852 i == Xball_2B ? j + 8 :
7853 i == Yball_eat ? j + 1 :
7854 i == Ykey_1_eat ? j + 1 :
7855 i == Ykey_2_eat ? j + 1 :
7856 i == Ykey_3_eat ? j + 1 :
7857 i == Ykey_4_eat ? j + 1 :
7858 i == Ykey_5_eat ? j + 1 :
7859 i == Ykey_6_eat ? j + 1 :
7860 i == Ykey_7_eat ? j + 1 :
7861 i == Ykey_8_eat ? j + 1 :
7862 i == Ylenses_eat ? j + 1 :
7863 i == Ymagnify_eat ? j + 1 :
7864 i == Ygrass_eat ? j + 1 :
7865 i == Ydirt_eat ? j + 1 :
7866 i == Xamoeba_1 ? 0 :
7867 i == Xamoeba_2 ? 1 :
7868 i == Xamoeba_3 ? 2 :
7869 i == Xamoeba_4 ? 3 :
7870 i == Xamoeba_5 ? 0 :
7871 i == Xamoeba_6 ? 1 :
7872 i == Xamoeba_7 ? 2 :
7873 i == Xamoeba_8 ? 3 :
7874 i == Xexit_2 ? j + 8 :
7875 i == Xexit_3 ? j + 16 :
7876 i == Xdynamite_1 ? 0 :
7877 i == Xdynamite_2 ? 8 :
7878 i == Xdynamite_3 ? 16 :
7879 i == Xdynamite_4 ? 24 :
7880 i == Xsand_stonein_1 ? j + 1 :
7881 i == Xsand_stonein_2 ? j + 9 :
7882 i == Xsand_stonein_3 ? j + 17 :
7883 i == Xsand_stonein_4 ? j + 25 :
7884 i == Xsand_stoneout_1 && j == 0 ? 0 :
7885 i == Xsand_stoneout_1 && j == 1 ? 0 :
7886 i == Xsand_stoneout_1 && j == 2 ? 1 :
7887 i == Xsand_stoneout_1 && j == 3 ? 2 :
7888 i == Xsand_stoneout_1 && j == 4 ? 2 :
7889 i == Xsand_stoneout_1 && j == 5 ? 3 :
7890 i == Xsand_stoneout_1 && j == 6 ? 4 :
7891 i == Xsand_stoneout_1 && j == 7 ? 4 :
7892 i == Xsand_stoneout_2 && j == 0 ? 5 :
7893 i == Xsand_stoneout_2 && j == 1 ? 6 :
7894 i == Xsand_stoneout_2 && j == 2 ? 7 :
7895 i == Xsand_stoneout_2 && j == 3 ? 8 :
7896 i == Xsand_stoneout_2 && j == 4 ? 9 :
7897 i == Xsand_stoneout_2 && j == 5 ? 11 :
7898 i == Xsand_stoneout_2 && j == 6 ? 13 :
7899 i == Xsand_stoneout_2 && j == 7 ? 15 :
7900 i == Xboom_bug && j == 1 ? 2 :
7901 i == Xboom_bug && j == 2 ? 2 :
7902 i == Xboom_bug && j == 3 ? 4 :
7903 i == Xboom_bug && j == 4 ? 4 :
7904 i == Xboom_bug && j == 5 ? 2 :
7905 i == Xboom_bug && j == 6 ? 2 :
7906 i == Xboom_bug && j == 7 ? 0 :
7907 i == Xboom_bomb && j == 1 ? 2 :
7908 i == Xboom_bomb && j == 2 ? 2 :
7909 i == Xboom_bomb && j == 3 ? 4 :
7910 i == Xboom_bomb && j == 4 ? 4 :
7911 i == Xboom_bomb && j == 5 ? 2 :
7912 i == Xboom_bomb && j == 6 ? 2 :
7913 i == Xboom_bomb && j == 7 ? 0 :
7914 i == Xboom_android && j == 7 ? 6 :
7915 i == Xboom_1 && j == 1 ? 2 :
7916 i == Xboom_1 && j == 2 ? 2 :
7917 i == Xboom_1 && j == 3 ? 4 :
7918 i == Xboom_1 && j == 4 ? 4 :
7919 i == Xboom_1 && j == 5 ? 6 :
7920 i == Xboom_1 && j == 6 ? 6 :
7921 i == Xboom_1 && j == 7 ? 8 :
7922 i == Xboom_2 && j == 0 ? 8 :
7923 i == Xboom_2 && j == 1 ? 8 :
7924 i == Xboom_2 && j == 2 ? 10 :
7925 i == Xboom_2 && j == 3 ? 10 :
7926 i == Xboom_2 && j == 4 ? 10 :
7927 i == Xboom_2 && j == 5 ? 12 :
7928 i == Xboom_2 && j == 6 ? 12 :
7929 i == Xboom_2 && j == 7 ? 12 :
7930 special_animation && j == 4 ? 3 :
7931 effective_action != action ? 0 :
7935 Bitmap *debug_bitmap = g_em->bitmap;
7936 int debug_src_x = g_em->src_x;
7937 int debug_src_y = g_em->src_y;
7940 int frame = getAnimationFrame(g->anim_frames,
7943 g->anim_start_frame,
7946 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7947 g->double_movement && is_backside);
7949 g_em->bitmap = src_bitmap;
7950 g_em->src_x = src_x;
7951 g_em->src_y = src_y;
7952 g_em->src_offset_x = 0;
7953 g_em->src_offset_y = 0;
7954 g_em->dst_offset_x = 0;
7955 g_em->dst_offset_y = 0;
7956 g_em->width = TILEX;
7957 g_em->height = TILEY;
7959 g_em->preserve_background = FALSE;
7961 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7964 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7965 effective_action == ACTION_MOVING ||
7966 effective_action == ACTION_PUSHING ||
7967 effective_action == ACTION_EATING)) ||
7968 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7969 effective_action == ACTION_EMPTYING)))
7972 (effective_action == ACTION_FALLING ||
7973 effective_action == ACTION_FILLING ||
7974 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7975 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7976 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7977 int num_steps = (i == Ydrip_s1 ? 16 :
7978 i == Ydrip_s1B ? 16 :
7979 i == Ydrip_s2 ? 16 :
7980 i == Ydrip_s2B ? 16 :
7981 i == Xsand_stonein_1 ? 32 :
7982 i == Xsand_stonein_2 ? 32 :
7983 i == Xsand_stonein_3 ? 32 :
7984 i == Xsand_stonein_4 ? 32 :
7985 i == Xsand_stoneout_1 ? 16 :
7986 i == Xsand_stoneout_2 ? 16 : 8);
7987 int cx = ABS(dx) * (TILEX / num_steps);
7988 int cy = ABS(dy) * (TILEY / num_steps);
7989 int step_frame = (i == Ydrip_s2 ? j + 8 :
7990 i == Ydrip_s2B ? j + 8 :
7991 i == Xsand_stonein_2 ? j + 8 :
7992 i == Xsand_stonein_3 ? j + 16 :
7993 i == Xsand_stonein_4 ? j + 24 :
7994 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7995 int step = (is_backside ? step_frame : num_steps - step_frame);
7997 if (is_backside) /* tile where movement starts */
7999 if (dx < 0 || dy < 0)
8001 g_em->src_offset_x = cx * step;
8002 g_em->src_offset_y = cy * step;
8006 g_em->dst_offset_x = cx * step;
8007 g_em->dst_offset_y = cy * step;
8010 else /* tile where movement ends */
8012 if (dx < 0 || dy < 0)
8014 g_em->dst_offset_x = cx * step;
8015 g_em->dst_offset_y = cy * step;
8019 g_em->src_offset_x = cx * step;
8020 g_em->src_offset_y = cy * step;
8024 g_em->width = TILEX - cx * step;
8025 g_em->height = TILEY - cy * step;
8028 /* create unique graphic identifier to decide if tile must be redrawn */
8029 /* bit 31 - 16 (16 bit): EM style graphic
8030 bit 15 - 12 ( 4 bit): EM style frame
8031 bit 11 - 6 ( 6 bit): graphic width
8032 bit 5 - 0 ( 6 bit): graphic height */
8033 g_em->unique_identifier =
8034 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8038 /* skip check for EMC elements not contained in original EMC artwork */
8039 if (element == EL_EMC_FAKE_ACID)
8042 if (g_em->bitmap != debug_bitmap ||
8043 g_em->src_x != debug_src_x ||
8044 g_em->src_y != debug_src_y ||
8045 g_em->src_offset_x != 0 ||
8046 g_em->src_offset_y != 0 ||
8047 g_em->dst_offset_x != 0 ||
8048 g_em->dst_offset_y != 0 ||
8049 g_em->width != TILEX ||
8050 g_em->height != TILEY)
8052 static int last_i = -1;
8060 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8061 i, element, element_info[element].token_name,
8062 element_action_info[effective_action].suffix, direction);
8064 if (element != effective_element)
8065 printf(" [%d ('%s')]",
8067 element_info[effective_element].token_name);
8071 if (g_em->bitmap != debug_bitmap)
8072 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8073 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8075 if (g_em->src_x != debug_src_x ||
8076 g_em->src_y != debug_src_y)
8077 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8078 j, (is_backside ? 'B' : 'F'),
8079 g_em->src_x, g_em->src_y,
8080 g_em->src_x / 32, g_em->src_y / 32,
8081 debug_src_x, debug_src_y,
8082 debug_src_x / 32, debug_src_y / 32);
8084 if (g_em->src_offset_x != 0 ||
8085 g_em->src_offset_y != 0 ||
8086 g_em->dst_offset_x != 0 ||
8087 g_em->dst_offset_y != 0)
8088 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8090 g_em->src_offset_x, g_em->src_offset_y,
8091 g_em->dst_offset_x, g_em->dst_offset_y);
8093 if (g_em->width != TILEX ||
8094 g_em->height != TILEY)
8095 printf(" %d (%d): size %d,%d should be %d,%d\n",
8097 g_em->width, g_em->height, TILEX, TILEY);
8099 num_em_gfx_errors++;
8106 for (i = 0; i < TILE_MAX; i++)
8108 for (j = 0; j < 8; j++)
8110 int element = object_mapping[i].element_rnd;
8111 int action = object_mapping[i].action;
8112 int direction = object_mapping[i].direction;
8113 boolean is_backside = object_mapping[i].is_backside;
8114 int graphic_action = el_act_dir2img(element, action, direction);
8115 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8117 if ((action == ACTION_SMASHED_BY_ROCK ||
8118 action == ACTION_SMASHED_BY_SPRING ||
8119 action == ACTION_EATING) &&
8120 graphic_action == graphic_default)
8122 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8123 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8124 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8125 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8128 /* no separate animation for "smashed by rock" -- use rock instead */
8129 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8130 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8132 g_em->bitmap = g_xx->bitmap;
8133 g_em->src_x = g_xx->src_x;
8134 g_em->src_y = g_xx->src_y;
8135 g_em->src_offset_x = g_xx->src_offset_x;
8136 g_em->src_offset_y = g_xx->src_offset_y;
8137 g_em->dst_offset_x = g_xx->dst_offset_x;
8138 g_em->dst_offset_y = g_xx->dst_offset_y;
8139 g_em->width = g_xx->width;
8140 g_em->height = g_xx->height;
8141 g_em->unique_identifier = g_xx->unique_identifier;
8144 g_em->preserve_background = TRUE;
8149 for (p = 0; p < MAX_PLAYERS; p++)
8151 for (i = 0; i < SPR_MAX; i++)
8153 int element = player_mapping[p][i].element_rnd;
8154 int action = player_mapping[p][i].action;
8155 int direction = player_mapping[p][i].direction;
8157 for (j = 0; j < 8; j++)
8159 int effective_element = element;
8160 int effective_action = action;
8161 int graphic = (direction == MV_NONE ?
8162 el_act2img(effective_element, effective_action) :
8163 el_act_dir2img(effective_element, effective_action,
8165 struct GraphicInfo *g = &graphic_info[graphic];
8166 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8172 Bitmap *debug_bitmap = g_em->bitmap;
8173 int debug_src_x = g_em->src_x;
8174 int debug_src_y = g_em->src_y;
8177 int frame = getAnimationFrame(g->anim_frames,
8180 g->anim_start_frame,
8183 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8185 g_em->bitmap = src_bitmap;
8186 g_em->src_x = src_x;
8187 g_em->src_y = src_y;
8188 g_em->src_offset_x = 0;
8189 g_em->src_offset_y = 0;
8190 g_em->dst_offset_x = 0;
8191 g_em->dst_offset_y = 0;
8192 g_em->width = TILEX;
8193 g_em->height = TILEY;
8197 /* skip check for EMC elements not contained in original EMC artwork */
8198 if (element == EL_PLAYER_3 ||
8199 element == EL_PLAYER_4)
8202 if (g_em->bitmap != debug_bitmap ||
8203 g_em->src_x != debug_src_x ||
8204 g_em->src_y != debug_src_y)
8206 static int last_i = -1;
8214 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8215 p, i, element, element_info[element].token_name,
8216 element_action_info[effective_action].suffix, direction);
8218 if (element != effective_element)
8219 printf(" [%d ('%s')]",
8221 element_info[effective_element].token_name);
8225 if (g_em->bitmap != debug_bitmap)
8226 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8227 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8229 if (g_em->src_x != debug_src_x ||
8230 g_em->src_y != debug_src_y)
8231 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8233 g_em->src_x, g_em->src_y,
8234 g_em->src_x / 32, g_em->src_y / 32,
8235 debug_src_x, debug_src_y,
8236 debug_src_x / 32, debug_src_y / 32);
8238 num_em_gfx_errors++;
8248 printf("::: [%d errors found]\n", num_em_gfx_errors);
8254 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8255 boolean any_player_moving,
8256 boolean any_player_snapping,
8257 boolean any_player_dropping)
8259 if (frame == 0 && !any_player_dropping)
8261 if (!local_player->was_waiting)
8263 if (!CheckSaveEngineSnapshotToList())
8266 local_player->was_waiting = TRUE;
8269 else if (any_player_moving || any_player_snapping || any_player_dropping)
8271 local_player->was_waiting = FALSE;
8275 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8276 boolean murphy_is_dropping)
8278 if (murphy_is_waiting)
8280 if (!local_player->was_waiting)
8282 if (!CheckSaveEngineSnapshotToList())
8285 local_player->was_waiting = TRUE;
8290 local_player->was_waiting = FALSE;
8294 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8295 boolean any_player_moving,
8296 boolean any_player_snapping,
8297 boolean any_player_dropping)
8299 if (tape.single_step && tape.recording && !tape.pausing)
8300 if (frame == 0 && !any_player_dropping)
8301 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8303 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8304 any_player_snapping, any_player_dropping);
8307 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8308 boolean murphy_is_dropping)
8310 if (tape.single_step && tape.recording && !tape.pausing)
8311 if (murphy_is_waiting)
8312 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8314 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8317 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8318 int graphic, int sync_frame, int x, int y)
8320 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8322 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8325 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8327 return (IS_NEXT_FRAME(sync_frame, graphic));
8330 int getGraphicInfo_Delay(int graphic)
8332 return graphic_info[graphic].anim_delay;
8335 void PlayMenuSoundExt(int sound)
8337 if (sound == SND_UNDEFINED)
8340 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8341 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8344 if (IS_LOOP_SOUND(sound))
8345 PlaySoundLoop(sound);
8350 void PlayMenuSound()
8352 PlayMenuSoundExt(menu.sound[game_status]);
8355 void PlayMenuSoundStereo(int sound, int stereo_position)
8357 if (sound == SND_UNDEFINED)
8360 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8361 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8364 if (IS_LOOP_SOUND(sound))
8365 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8367 PlaySoundStereo(sound, stereo_position);
8370 void PlayMenuSoundIfLoopExt(int sound)
8372 if (sound == SND_UNDEFINED)
8375 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8376 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8379 if (IS_LOOP_SOUND(sound))
8380 PlaySoundLoop(sound);
8383 void PlayMenuSoundIfLoop()
8385 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8388 void PlayMenuMusicExt(int music)
8390 if (music == MUS_UNDEFINED)
8393 if (!setup.sound_music)
8399 void PlayMenuMusic()
8401 char *curr_music = getCurrentlyPlayingMusicFilename();
8402 char *next_music = getMusicListEntry(menu.music[game_status])->filename;
8404 if (!strEqual(curr_music, next_music))
8405 PlayMenuMusicExt(menu.music[game_status]);
8408 void PlayMenuSoundsAndMusic()
8414 static void FadeMenuSounds()
8419 static void FadeMenuMusic()
8421 char *curr_music = getCurrentlyPlayingMusicFilename();
8422 char *next_music = getMusicListEntry(menu.music[game_status])->filename;
8424 if (!strEqual(curr_music, next_music))
8428 void FadeMenuSoundsAndMusic()
8434 void PlaySoundActivating()
8437 PlaySound(SND_MENU_ITEM_ACTIVATING);
8441 void PlaySoundSelecting()
8444 PlaySound(SND_MENU_ITEM_SELECTING);
8448 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8450 boolean change_fullscreen = (setup.fullscreen !=
8451 video.fullscreen_enabled);
8452 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8453 setup.window_scaling_percent !=
8454 video.window_scaling_percent);
8456 if (change_window_scaling_percent && video.fullscreen_enabled)
8459 if (!change_window_scaling_percent && !video.fullscreen_available)
8462 #if defined(TARGET_SDL2)
8463 if (change_window_scaling_percent)
8465 SDLSetWindowScaling(setup.window_scaling_percent);
8469 else if (change_fullscreen)
8471 SDLSetWindowFullscreen(setup.fullscreen);
8473 /* set setup value according to successfully changed fullscreen mode */
8474 setup.fullscreen = video.fullscreen_enabled;
8480 if (change_fullscreen ||
8481 change_window_scaling_percent)
8483 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8485 /* save backbuffer content which gets lost when toggling fullscreen mode */
8486 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8488 if (change_window_scaling_percent)
8490 /* keep window mode, but change window scaling */
8491 video.fullscreen_enabled = TRUE; /* force new window scaling */
8494 /* toggle fullscreen */
8495 ChangeVideoModeIfNeeded(setup.fullscreen);
8497 /* set setup value according to successfully changed fullscreen mode */
8498 setup.fullscreen = video.fullscreen_enabled;
8500 /* restore backbuffer content from temporary backbuffer backup bitmap */
8501 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8503 FreeBitmap(tmp_backbuffer);
8505 /* update visible window/screen */
8506 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8510 void JoinRectangles(int *x, int *y, int *width, int *height,
8511 int x2, int y2, int width2, int height2)
8513 // do not join with "off-screen" rectangle
8514 if (x2 == -1 || y2 == -1)
8519 *width = MAX(*width, width2);
8520 *height = MAX(*height, height2);
8523 void SetAnimStatus(int anim_status_new)
8525 if (anim_status_new == GAME_MODE_MAIN)
8526 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8528 global.anim_status_next = anim_status_new;
8530 // directly set screen modes that are entered without fading
8531 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8532 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8533 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8534 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8535 global.anim_status = global.anim_status_next;
8538 void SetGameStatus(int game_status_new)
8540 if (game_status_new != game_status)
8541 game_status_last_screen = game_status;
8543 game_status = game_status_new;
8545 SetAnimStatus(game_status_new);
8548 void SetFontStatus(int game_status_new)
8550 static int last_game_status = -1;
8552 if (game_status_new != -1)
8554 // set game status for font use after storing last game status
8555 last_game_status = game_status;
8556 game_status = game_status_new;
8560 // reset game status after font use from last stored game status
8561 game_status = last_game_status;
8565 void ResetFontStatus()
8570 void ChangeViewportPropertiesIfNeeded()
8572 int gfx_game_mode = game_status;
8573 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8575 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8576 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8577 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8578 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8579 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8580 int new_win_xsize = vp_window->width;
8581 int new_win_ysize = vp_window->height;
8582 int border_size = vp_playfield->border_size;
8583 int new_sx = vp_playfield->x + border_size;
8584 int new_sy = vp_playfield->y + border_size;
8585 int new_sxsize = vp_playfield->width - 2 * border_size;
8586 int new_sysize = vp_playfield->height - 2 * border_size;
8587 int new_real_sx = vp_playfield->x;
8588 int new_real_sy = vp_playfield->y;
8589 int new_full_sxsize = vp_playfield->width;
8590 int new_full_sysize = vp_playfield->height;
8591 int new_dx = vp_door_1->x;
8592 int new_dy = vp_door_1->y;
8593 int new_dxsize = vp_door_1->width;
8594 int new_dysize = vp_door_1->height;
8595 int new_vx = vp_door_2->x;
8596 int new_vy = vp_door_2->y;
8597 int new_vxsize = vp_door_2->width;
8598 int new_vysize = vp_door_2->height;
8599 int new_ex = vp_door_3->x;
8600 int new_ey = vp_door_3->y;
8601 int new_exsize = vp_door_3->width;
8602 int new_eysize = vp_door_3->height;
8603 int new_tilesize_var =
8604 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8606 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8607 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8608 int new_scr_fieldx = new_sxsize / tilesize;
8609 int new_scr_fieldy = new_sysize / tilesize;
8610 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8611 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8612 boolean init_gfx_buffers = FALSE;
8613 boolean init_video_buffer = FALSE;
8614 boolean init_gadgets_and_anims = FALSE;
8615 boolean init_em_graphics = FALSE;
8617 if (new_win_xsize != WIN_XSIZE ||
8618 new_win_ysize != WIN_YSIZE)
8620 WIN_XSIZE = new_win_xsize;
8621 WIN_YSIZE = new_win_ysize;
8623 init_video_buffer = TRUE;
8624 init_gfx_buffers = TRUE;
8625 init_gadgets_and_anims = TRUE;
8627 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8630 if (new_scr_fieldx != SCR_FIELDX ||
8631 new_scr_fieldy != SCR_FIELDY)
8633 /* this always toggles between MAIN and GAME when using small tile size */
8635 SCR_FIELDX = new_scr_fieldx;
8636 SCR_FIELDY = new_scr_fieldy;
8638 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8649 new_sxsize != SXSIZE ||
8650 new_sysize != SYSIZE ||
8651 new_dxsize != DXSIZE ||
8652 new_dysize != DYSIZE ||
8653 new_vxsize != VXSIZE ||
8654 new_vysize != VYSIZE ||
8655 new_exsize != EXSIZE ||
8656 new_eysize != EYSIZE ||
8657 new_real_sx != REAL_SX ||
8658 new_real_sy != REAL_SY ||
8659 new_full_sxsize != FULL_SXSIZE ||
8660 new_full_sysize != FULL_SYSIZE ||
8661 new_tilesize_var != TILESIZE_VAR
8664 // ------------------------------------------------------------------------
8665 // determine next fading area for changed viewport definitions
8666 // ------------------------------------------------------------------------
8668 // start with current playfield area (default fading area)
8671 FADE_SXSIZE = FULL_SXSIZE;
8672 FADE_SYSIZE = FULL_SYSIZE;
8674 // add new playfield area if position or size has changed
8675 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8676 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8678 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8679 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8682 // add current and new door 1 area if position or size has changed
8683 if (new_dx != DX || new_dy != DY ||
8684 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8686 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8687 DX, DY, DXSIZE, DYSIZE);
8688 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8689 new_dx, new_dy, new_dxsize, new_dysize);
8692 // add current and new door 2 area if position or size has changed
8693 if (new_dx != VX || new_dy != VY ||
8694 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8696 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8697 VX, VY, VXSIZE, VYSIZE);
8698 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8699 new_vx, new_vy, new_vxsize, new_vysize);
8702 // ------------------------------------------------------------------------
8703 // handle changed tile size
8704 // ------------------------------------------------------------------------
8706 if (new_tilesize_var != TILESIZE_VAR)
8708 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8710 // changing tile size invalidates scroll values of engine snapshots
8711 FreeEngineSnapshotSingle();
8713 // changing tile size requires update of graphic mapping for EM engine
8714 init_em_graphics = TRUE;
8725 SXSIZE = new_sxsize;
8726 SYSIZE = new_sysize;
8727 DXSIZE = new_dxsize;
8728 DYSIZE = new_dysize;
8729 VXSIZE = new_vxsize;
8730 VYSIZE = new_vysize;
8731 EXSIZE = new_exsize;
8732 EYSIZE = new_eysize;
8733 REAL_SX = new_real_sx;
8734 REAL_SY = new_real_sy;
8735 FULL_SXSIZE = new_full_sxsize;
8736 FULL_SYSIZE = new_full_sysize;
8737 TILESIZE_VAR = new_tilesize_var;
8739 init_gfx_buffers = TRUE;
8740 init_gadgets_and_anims = TRUE;
8742 // printf("::: viewports: init_gfx_buffers\n");
8743 // printf("::: viewports: init_gadgets_and_anims\n");
8746 if (init_gfx_buffers)
8748 // printf("::: init_gfx_buffers\n");
8750 SCR_FIELDX = new_scr_fieldx_buffers;
8751 SCR_FIELDY = new_scr_fieldy_buffers;
8755 SCR_FIELDX = new_scr_fieldx;
8756 SCR_FIELDY = new_scr_fieldy;
8758 SetDrawDeactivationMask(REDRAW_NONE);
8759 SetDrawBackgroundMask(REDRAW_FIELD);
8762 if (init_video_buffer)
8764 // printf("::: init_video_buffer\n");
8766 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8767 InitImageTextures();
8770 if (init_gadgets_and_anims)
8772 // printf("::: init_gadgets_and_anims\n");
8775 InitGlobalAnimations();
8778 if (init_em_graphics)
8780 InitGraphicInfo_EM();